2635 lines · python
1#!/usr/bin/env python2 3import os4from builtins import range5from dataclasses import dataclass6from functools import reduce7from typing import (8 Any,9 Dict,10 List, # Needed for python 3.8 compatibility.11 NewType,12 Optional,13 Set,14)15import functools16import json17from libcxx.header_information import headers_not_available18 19 20def get_libcxx_paths():21 utils_path = os.path.dirname(os.path.abspath(__file__))22 script_name = os.path.basename(__file__)23 assert os.path.exists(utils_path)24 src_root = os.path.dirname(utils_path)25 include_path = os.path.join(src_root, "include")26 assert os.path.exists(include_path)27 docs_path = os.path.join(src_root, "docs")28 assert os.path.exists(docs_path)29 macro_test_path = os.path.join(30 src_root,31 "test",32 "std",33 "language.support",34 "support.limits",35 "support.limits.general",36 )37 assert os.path.exists(macro_test_path)38 assert os.path.exists(39 os.path.join(macro_test_path, "version.version.compile.pass.cpp")40 )41 return script_name, src_root, include_path, docs_path, macro_test_path42 43 44script_name, source_root, include_path, docs_path, macro_test_path = get_libcxx_paths()45 46 47def has_header(h):48 h_path = os.path.join(include_path, h)49 return os.path.exists(h_path)50 51 52def add_version_header(tc):53 tc["headers"].append("version")54 return tc55 56 57# ================ ============================================================58# Field Description59# ================ ============================================================60# name The name of the feature-test macro.61# values A dict whose keys are C++ versions and whose values are the62# value of the feature-test macro for that C++ version.63# (TODO: This isn't a very clean model for feature-test64# macros affected by multiple papers.)65# headers An array with the headers that should provide the66# feature-test macro.67# test_suite_guard An optional string field. When this field is provided,68# `libcxx_guard` must also be provided. This field is used69# only to generate the unit tests for the feature-test macros.70# It can't depend on macros defined in <__config> because the71# `test/std/` parts of the test suite are intended to be72# portable to any C++ standard library implementation, not73# just libc++. It may depend on74# * macros defined by the compiler itself, or75# * macros generated by CMake.76# In some cases we add also depend on macros defined in77# <__configuration/availability.h>.78# libcxx_guard An optional string field. When this field is provided,79# `test_suite_guard` must also be provided. This field is used80# only to guard the feature-test macro in <version>. It may81# be the same as `test_suite_guard`, or it may depend on82# macros defined in <__config>.83# unimplemented An optional Boolean field with the value `True`. This field84# is only used when a feature isn't fully implemented. Once85# you've fully implemented the feature, you should remove86# this field.87# ================ ============================================================88feature_test_macros = [89 add_version_header(x)90 for x in [91 {92 "name": "__cpp_lib_adaptor_iterator_pair_constructor",93 "values": {"c++23": 202106},94 "headers": ["queue", "stack"],95 },96 {97 "name": "__cpp_lib_addressof_constexpr",98 "values": {"c++17": 201603},99 "headers": ["memory"],100 },101 {102 "name": "__cpp_lib_aligned_accessor",103 "values": {"c++26": 202411},104 "headers": ["mdspan"],105 },106 {107 "name": "__cpp_lib_allocate_at_least",108 "values": {109 # Note LWG3887 Version macro for allocate_at_least110 "c++23": 202302, # P2652R2 Disallow User Specialization of allocator_traits111 },112 "headers": ["memory"],113 },114 {115 "name": "__cpp_lib_allocator_traits_is_always_equal",116 "values": {"c++17": 201411},117 "headers": [118 "deque",119 "forward_list",120 "list",121 "map",122 "memory",123 "scoped_allocator",124 "set",125 "string",126 "unordered_map",127 "unordered_set",128 "vector",129 ],130 },131 {132 "name": "__cpp_lib_any",133 "values": {"c++17": 201606},134 "headers": ["any"],135 },136 {137 "name": "__cpp_lib_apply",138 "values": {"c++17": 201603},139 "headers": ["tuple"],140 },141 {142 "name": "__cpp_lib_array_constexpr",143 "values": {"c++17": 201603, "c++20": 201811},144 "headers": ["array", "iterator"],145 },146 {147 "name": "__cpp_lib_as_const",148 "values": {"c++17": 201510},149 "headers": ["utility"],150 },151 {152 "name": "__cpp_lib_associative_heterogeneous_erasure",153 "values": {"c++23": 202110},154 "headers": ["map", "set", "unordered_map", "unordered_set"],155 "unimplemented": True,156 },157 {158 "name": "__cpp_lib_associative_heterogeneous_insertion",159 "values": {160 "c++26": 202306 # P2363R5 Extending associative containers with the remaining heterogeneous overloads161 },162 "headers": ["map", "set", "unordered_map", "unordered_set"],163 "unimplemented": True,164 },165 {166 "name": "__cpp_lib_assume_aligned",167 "values": {"c++20": 201811},168 "headers": ["memory"],169 },170 {171 "name": "__cpp_lib_atomic_flag_test",172 "values": {"c++20": 201907},173 "headers": ["atomic"],174 },175 {176 "name": "__cpp_lib_atomic_float",177 "values": {"c++20": 201711},178 "headers": ["atomic"],179 },180 {181 "name": "__cpp_lib_atomic_is_always_lock_free",182 "values": {"c++17": 201603},183 "headers": ["atomic"],184 },185 {186 "name": "__cpp_lib_atomic_lock_free_type_aliases",187 "values": {"c++20": 201907},188 "headers": ["atomic"],189 },190 {191 "name": "__cpp_lib_atomic_min_max",192 "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum193 "headers": ["atomic"],194 "unimplemented": True,195 },196 {197 "name": "__cpp_lib_atomic_ref",198 "values": {199 "c++20": 201806,200 "c++26": 202411, # P2835R7: Expose std::atomic_ref 's object address201 },202 "headers": ["atomic"],203 },204 {205 "name": "__cpp_lib_atomic_shared_ptr",206 "values": {"c++20": 201711},207 "headers": ["atomic"],208 "unimplemented": True,209 },210 {211 "name": "__cpp_lib_atomic_value_initialization",212 "values": {"c++20": 201911},213 "headers": ["atomic", "memory"],214 },215 {216 "name": "__cpp_lib_atomic_wait",217 "values": {"c++20": 201907},218 "headers": ["atomic"],219 },220 {221 "name": "__cpp_lib_barrier",222 "values": {"c++20": 201907},223 "headers": ["barrier"],224 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",225 "libcxx_guard": "_LIBCPP_HAS_THREADS",226 },227 {228 "name": "__cpp_lib_bind_back",229 "values": {230 "c++23": 202202,231 # "c++26": 202306, # P2714R1 Bind front and back to NTTP callables232 },233 "headers": ["functional"],234 },235 {236 "name": "__cpp_lib_bind_front",237 "values": {238 "c++20": 201907,239 "c++26": 202306, # P2714R1 Bind front and back to NTTP callables240 },241 "headers": ["functional"],242 },243 {244 "name": "__cpp_lib_bit_cast",245 "values": {"c++20": 201806},246 "headers": ["bit"],247 },248 {249 "name": "__cpp_lib_bitops",250 "values": {"c++20": 201907},251 "headers": ["bit"],252 },253 {254 "name": "__cpp_lib_bitset",255 "values": {"c++26": 202306}, # P2697R1 Interfacing bitset with string_view256 "headers": ["bitset"],257 },258 {259 "name": "__cpp_lib_bool_constant",260 "values": {"c++17": 201505},261 "headers": ["type_traits"],262 },263 {264 "name": "__cpp_lib_bounded_array_traits",265 "values": {"c++20": 201902},266 "headers": ["type_traits"],267 },268 {269 "name": "__cpp_lib_boyer_moore_searcher",270 "values": {"c++17": 201603},271 "headers": ["functional"],272 },273 {274 "name": "__cpp_lib_byte",275 "values": {"c++17": 201603},276 "headers": ["cstddef"],277 },278 {279 "name": "__cpp_lib_byteswap",280 "values": {"c++23": 202110},281 "headers": ["bit"],282 },283 {284 "name": "__cpp_lib_char8_t",285 "values": {"c++20": 201907},286 "headers": [287 "atomic",288 "filesystem",289 "istream",290 "limits",291 "locale",292 "ostream",293 "string",294 "string_view",295 ],296 "test_suite_guard": "defined(__cpp_char8_t)",297 "libcxx_guard": "_LIBCPP_HAS_CHAR8_T",298 },299 {300 "name": "__cpp_lib_chrono",301 "values": {302 "c++17": 201611,303 # "c++26": 202306, # P2592R3 Hashing support for std::chrono value classes304 },305 "headers": ["chrono"],306 },307 {308 "name": "__cpp_lib_chrono_udls",309 "values": {"c++14": 201304},310 "headers": ["chrono"],311 },312 {313 "name": "__cpp_lib_clamp",314 "values": {"c++17": 201603},315 "headers": ["algorithm"],316 },317 {318 "name": "__cpp_lib_common_reference",319 "values": {"c++20": 202302},320 "headers": ["type_traits"],321 },322 {323 "name": "__cpp_lib_common_reference_wrapper",324 "values": {"c++20": 202302},325 "headers": ["functional"],326 },327 {328 "name": "__cpp_lib_complex_udls",329 "values": {"c++14": 201309},330 "headers": ["complex"],331 },332 {333 "name": "__cpp_lib_concepts",334 "values": {"c++20": 202002},335 "headers": ["concepts"],336 },337 {338 "name": "__cpp_lib_constexpr_algorithms",339 "values": {340 "c++20": 201806,341 "c++26": 202306,342 },343 "headers": ["algorithm", "utility"],344 },345 {346 "name": "__cpp_lib_constexpr_bitset",347 "values": {"c++23": 202207},348 "headers": ["bitset"],349 },350 {351 "name": "__cpp_lib_constexpr_charconv",352 "values": {"c++23": 202207},353 "headers": ["charconv"],354 },355 {356 "name": "__cpp_lib_constexpr_cmath",357 "values": {"c++23": 202202},358 "headers": ["cmath", "cstdlib"],359 "unimplemented": True,360 },361 {362 "name": "__cpp_lib_constexpr_complex",363 "values": {"c++20": 201711},364 "headers": ["complex"],365 },366 {367 "name": "__cpp_lib_constexpr_dynamic_alloc",368 "values": {"c++20": 201907},369 "headers": ["memory"],370 },371 {372 "name": "__cpp_lib_constexpr_flat_map",373 "values": {"c++26": 202502},374 "headers": ["flat_map"],375 },376 {377 "name": "__cpp_lib_constexpr_flat_set",378 "values": {"c++26": 202502},379 "headers": ["flat_set"],380 },381 {382 "name": "__cpp_lib_constexpr_forward_list",383 "values": {"c++26": 202502},384 "headers": ["forward_list"],385 },386 {387 "name": "__cpp_lib_constexpr_functional",388 "values": {"c++20": 201907},389 "headers": ["functional"],390 },391 {392 "name": "__cpp_lib_constexpr_iterator",393 "values": {"c++20": 201811},394 "headers": ["iterator"],395 },396 {397 "name": "__cpp_lib_constexpr_list",398 "values": {"c++26": 202502},399 "headers": ["list"],400 },401 {402 "name": "__cpp_lib_constexpr_memory",403 "values": {"c++20": 201811, "c++23": 202202},404 "headers": ["memory"],405 },406 {407 "name": "__cpp_lib_constexpr_new",408 "values": {"c++26": 202406}, # P2747R2 constexpr placement new409 "headers": ["new"],410 "test_suite_guard": "!defined(_LIBCPP_ABI_VCRUNTIME)",411 "libcxx_guard": "!defined(_LIBCPP_ABI_VCRUNTIME)",412 },413 {414 "name": "__cpp_lib_constexpr_numeric",415 "values": {"c++20": 201911},416 "headers": ["numeric"],417 },418 {419 "name": "__cpp_lib_constexpr_queue",420 "values": {"c++26": 202502},421 "headers": ["queue"],422 },423 {424 "name": "__cpp_lib_constexpr_string",425 "values": {"c++20": 201907},426 "headers": ["string"],427 },428 {429 "name": "__cpp_lib_constexpr_string_view",430 "values": {"c++20": 201811},431 "headers": ["string_view"],432 },433 {434 "name": "__cpp_lib_constexpr_tuple",435 "values": {"c++20": 201811},436 "headers": ["tuple"],437 },438 {439 "name": "__cpp_lib_constexpr_typeinfo",440 "values": {"c++23": 202106},441 "headers": ["typeinfo"],442 },443 {444 "name": "__cpp_lib_constexpr_utility",445 "values": {"c++20": 201811},446 "headers": ["utility"],447 },448 {449 "name": "__cpp_lib_constexpr_vector",450 "values": {"c++20": 201907},451 "headers": ["vector"],452 },453 {454 "name": "__cpp_lib_constrained_equality",455 "values": {456 "c++26": 202411, # P3379R0: Constrain std::expected equality operators457 },458 "headers": ["expected", "optional", "tuple", "utility", "variant"],459 },460 {461 "name": "__cpp_lib_containers_ranges",462 "values": {"c++23": 202202},463 "headers": [464 "deque",465 "forward_list",466 "list",467 "map",468 "queue",469 "set",470 "stack",471 "string",472 "unordered_map",473 "unordered_set",474 "vector",475 ],476 },477 {478 "name": "__cpp_lib_copyable_function",479 "values": {"c++26": 202306}, # P2548R6 copyable_function480 "headers": ["functional"],481 "unimplemented": True,482 },483 {484 "name": "__cpp_lib_coroutine",485 "values": {"c++20": 201902},486 "headers": ["coroutine"],487 },488 {489 "name": "__cpp_lib_debugging",490 "values": {491 "c++26": 202311, # P2546R5 Debugging Support492 # "c++26": 202403, # P2810R4: is_debugger_present is_replaceable493 },494 "headers": ["debugging"],495 "unimplemented": True,496 },497 {498 "name": "__cpp_lib_default_template_type_for_algorithm_values",499 "values": {"c++26": 202403}, # P2248R8: Enabling list-initialization for algorithms500 "headers": ["algorithm", "deque", "forward_list", "list", "ranges", "string", "vector"],501 "unimplemented": True,502 },503 {504 "name": "__cpp_lib_destroying_delete",505 "values": {"c++20": 201806},506 "headers": ["new"],507 "test_suite_guard": "TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L",508 "libcxx_guard": "_LIBCPP_STD_VER >= 20 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L",509 },510 {511 "name": "__cpp_lib_enable_shared_from_this",512 "values": {"c++17": 201603},513 "headers": ["memory"],514 },515 {516 "name": "__cpp_lib_endian",517 "values": {"c++20": 201907},518 "headers": ["bit"],519 },520 {521 "name": "__cpp_lib_erase_if",522 "values": {"c++20": 202002},523 "headers": [524 "deque",525 "forward_list",526 "list",527 "map",528 "set",529 "string",530 "unordered_map",531 "unordered_set",532 "vector",533 ],534 },535 {536 "name": "__cpp_lib_exchange_function",537 "values": {"c++14": 201304},538 "headers": ["utility"],539 },540 {541 "name": "__cpp_lib_execution",542 "values": {"c++17": 201603, "c++20": 201902},543 "headers": ["execution"],544 "unimplemented": True,545 },546 {547 "name": "__cpp_lib_expected",548 "values": {"c++23": 202211},549 "headers": ["expected"],550 },551 {552 "name": "__cpp_lib_filesystem",553 "values": {"c++17": 201703},554 "headers": ["filesystem"],555 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_FILESYSTEM",556 "libcxx_guard": "_LIBCPP_HAS_FILESYSTEM",557 },558 {559 "name": "__cpp_lib_flat_map",560 "values": {"c++23": 202207},561 "headers": ["flat_map"],562 },563 {564 "name": "__cpp_lib_flat_set",565 "values": {"c++23": 202207},566 "headers": ["flat_set"],567 },568 {569 "name": "__cpp_lib_format",570 "values": {571 "c++20": 202110,572 # "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types573 # "c++26": 202306, P2637R3 Member Visit (implemented)574 # "c++26": 202311, P2918R2 Runtime format strings II (implemented)575 },576 # Note these three papers are adopted at the June 2023 meeting and have sequential numbering577 # 202304 P2510R3 Formatting pointers (Implemented)578 # 202305 P2757R3 Type-checking format args579 # 202306 P2637R3 Member Visit580 "headers": ["format"],581 # Trying to use `std::format` where to_chars floating-point is not582 # available causes compilation errors, even with non floating-point types.583 # https://llvm.org/PR125353584 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT",585 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT",586 },587 {588 "name": "__cpp_lib_format_path",589 "values": {"c++26": 202403}, # P2845R8: Formatting of std::filesystem::path590 "headers": ["filesystem"],591 "unimplemented": True,592 },593 {594 "name": "__cpp_lib_format_ranges",595 "values": {"c++23": 202207},596 "headers": ["format"],597 },598 {599 "name": "__cpp_lib_format_uchar",600 "values": {601 "c++20": 202311 # DR P2909R4 Fix formatting of code units as integers602 },603 "headers": [604 "format" # TODO verify this entry since the paper was underspecified.605 ],606 },607 {608 "name": "__cpp_lib_formatters",609 "values": {"c++23": 202302},610 "headers": ["stacktrace", "thread"],611 "unimplemented": True,612 },613 {614 "name": "__cpp_lib_forward_like",615 "values": {"c++23": 202207},616 "headers": ["utility"],617 },618 {619 "name": "__cpp_lib_freestanding_algorithm",620 "values": {621 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes622 },623 "headers": ["algorithm"],624 "unimplemented": True,625 },626 {627 "name": "__cpp_lib_freestanding_array",628 "values": {629 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes630 },631 "headers": ["array"],632 "unimplemented": True,633 },634 {635 "name": "__cpp_lib_freestanding_cstring",636 "values": {637 "c++26": 202306 # P2338R4 Freestanding Library: Character primitives and the C library638 # 202311 # P2407R5 Freestanding Library: Partial Classes639 },640 "headers": ["cstring"],641 "unimplemented": True,642 },643 {644 "name": "__cpp_lib_freestanding_expected",645 "values": {646 "c++26": 202311 # P2833R2 Freestanding Library: inout expected span647 },648 "headers": ["expected"],649 "unimplemented": True,650 },651 {652 "name": "__cpp_lib_freestanding_mdspan",653 "values": {654 "c++26": 202311 # P2833R2 Freestanding Library: inout expected span655 },656 "headers": ["mdspan"],657 "unimplemented": True,658 },659 {660 "name": "__cpp_lib_freestanding_optional",661 "values": {662 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes663 },664 "headers": ["optional"],665 "unimplemented": True,666 },667 {668 "name": "__cpp_lib_freestanding_string_view",669 "values": {670 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes671 },672 "headers": ["string_view"],673 "unimplemented": True,674 },675 {676 "name": "__cpp_lib_freestanding_variant",677 "values": {678 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes679 },680 "headers": ["variant"],681 "unimplemented": True,682 },683 {684 "name": "__cpp_lib_fstream_native_handle",685 "values": {"c++26": 202306}, # P1759R6 Native handles and file streams686 "headers": ["fstream"],687 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION)",688 "libcxx_guard": "_LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION",689 },690 {691 "name": "__cpp_lib_function_ref",692 "values": {693 "c++26": 202306 # P0792R14 function_ref: a type-erased callable reference694 },695 "headers": ["functional"],696 "unimplemented": True,697 },698 {699 "name": "__cpp_lib_gcd_lcm",700 "values": {"c++17": 201606},701 "headers": ["numeric"],702 },703 {704 "name": "__cpp_lib_generate_random",705 "values": {"c++26": 202403}, # P1068R11: Vector API for random number generation706 "headers": ["random"],707 "unimplemented": True,708 },709 {710 "name": "__cpp_lib_generic_associative_lookup",711 "values": {"c++14": 201304},712 "headers": ["map", "set"],713 },714 {715 "name": "__cpp_lib_generic_unordered_lookup",716 "values": {"c++20": 201811},717 "headers": ["unordered_map", "unordered_set"],718 },719 {720 "name": "__cpp_lib_hardware_interference_size",721 "values": {"c++17": 201703},722 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE))",723 "libcxx_guard": "defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE)",724 "headers": ["new"],725 },726 {727 "name": "__cpp_lib_has_unique_object_representations",728 "values": {"c++17": 201606},729 "headers": ["type_traits"],730 },731 {732 "name": "__cpp_lib_hazard_pointer",733 "values": {"c++26": 202306}, # P2530R3 Hazard Pointers for C++26734 "headers": [735 "hazard_pointer" # TODO verify this entry since the paper was underspecified.736 ],737 "unimplemented": True,738 },739 {740 "name": "__cpp_lib_hypot",741 "values": {"c++17": 201603},742 "headers": ["cmath"],743 },744 {745 "name": "__cpp_lib_incomplete_container_elements",746 "values": {"c++17": 201505},747 "headers": ["forward_list", "list", "vector"],748 },749 {750 "name": "__cpp_lib_inplace_vector",751 "values": {"c++26": 202406}, # P0843R14 inplace_vector752 "headers": ["inplace_vector"],753 "unimplemented": True,754 },755 {756 "name": "__cpp_lib_int_pow2",757 "values": {"c++20": 202002},758 "headers": ["bit"],759 },760 {761 "name": "__cpp_lib_integer_comparison_functions",762 "values": {"c++20": 202002},763 "headers": ["utility"],764 },765 {766 "name": "__cpp_lib_integer_sequence",767 "values": {"c++14": 201304},768 "headers": ["utility"],769 },770 {771 "name": "__cpp_lib_integral_constant_callable",772 "values": {"c++14": 201304},773 "headers": ["type_traits"],774 },775 {776 "name": "__cpp_lib_interpolate",777 "values": {"c++20": 201902},778 "headers": ["cmath", "numeric"],779 },780 {781 "name": "__cpp_lib_invoke",782 "values": {"c++17": 201411},783 "headers": ["functional"],784 },785 {786 "name": "__cpp_lib_invoke_r",787 "values": {"c++23": 202106},788 "headers": ["functional"],789 },790 {791 "name": "__cpp_lib_ios_noreplace",792 "values": {"c++23": 202207},793 "headers": ["ios"],794 },795 {796 "name": "__cpp_lib_is_aggregate",797 "values": {"c++17": 201703},798 "headers": ["type_traits"],799 },800 {801 "name": "__cpp_lib_is_constant_evaluated",802 "values": {"c++20": 201811},803 "headers": ["type_traits"],804 },805 {806 "name": "__cpp_lib_is_final",807 "values": {"c++14": 201402},808 "headers": ["type_traits"],809 },810 {811 "name": "__cpp_lib_is_implicit_lifetime",812 "values": {"c++23": 202302},813 "headers": ["type_traits"],814 "test_suite_guard": "__has_builtin(__builtin_is_implicit_lifetime)",815 "libcxx_guard": "__has_builtin(__builtin_is_implicit_lifetime)",816 },817 {818 "name": "__cpp_lib_is_invocable",819 "values": {"c++17": 201703},820 "headers": ["type_traits"],821 },822 {823 "name": "__cpp_lib_is_layout_compatible",824 "values": {"c++20": 201907},825 "headers": ["type_traits"],826 "unimplemented": True,827 },828 {829 "name": "__cpp_lib_is_nothrow_convertible",830 "values": {"c++20": 201806},831 "headers": ["type_traits"],832 },833 {834 "name": "__cpp_lib_is_null_pointer",835 "values": {"c++14": 201309},836 "headers": ["type_traits"],837 },838 {839 "name": "__cpp_lib_is_pointer_interconvertible",840 "values": {"c++20": 201907},841 "headers": ["type_traits"],842 "unimplemented": True,843 },844 {845 "name": "__cpp_lib_is_scoped_enum",846 "values": {"c++23": 202011},847 "headers": ["type_traits"],848 },849 {850 "name": "__cpp_lib_is_sufficiently_aligned",851 "values": {"c++26": 202411},852 "headers": ["memory"],853 },854 {855 "name": "__cpp_lib_is_swappable",856 "values": {"c++17": 201603},857 "headers": ["type_traits"],858 },859 {860 "name": "__cpp_lib_is_virtual_base_of",861 "values": {862 "c++26": 202406 # P2985R0 A type trait for detecting virtual base classes863 },864 "headers": ["type_traits"],865 "test_suite_guard": "__has_builtin(__builtin_is_virtual_base_of)",866 "libcxx_guard": "__has_builtin(__builtin_is_virtual_base_of)",867 },868 {869 "name": "__cpp_lib_is_within_lifetime",870 # Note this name was changed from "__cpp_lib_within_lifetime" when the paper was adopted871 # https://github.com/cplusplus/draft/commit/0facada4cadd97e1ba15bfaea76a804f1dc5c309872 "values": {873 "c++26": 202306 # P2641R4 Checking if a union alternative is active874 },875 "headers": ["type_traits"],876 "test_suite_guard": "__has_builtin(__builtin_is_within_lifetime)",877 "libcxx_guard": "__has_builtin(__builtin_is_within_lifetime)",878 },879 {880 "name": "__cpp_lib_jthread",881 "values": {"c++20": 201911},882 "headers": ["stop_token", "thread"],883 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",884 "libcxx_guard": "_LIBCPP_HAS_THREADS",885 },886 {887 "name": "__cpp_lib_latch",888 "values": {"c++20": 201907},889 "headers": ["latch"],890 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",891 "libcxx_guard": "_LIBCPP_HAS_THREADS",892 },893 {894 "name": "__cpp_lib_launder",895 "values": {"c++17": 201606},896 "headers": ["new"],897 },898 {899 "name": "__cpp_lib_linalg",900 "values": {901 "c++26": 202311 # P1673 A free function linear algebra interface based on the BLAS902 },903 "headers": ["linalg"],904 "unimplemented": True,905 },906 {907 "name": "__cpp_lib_list_remove_return_type",908 "values": {"c++20": 201806},909 "headers": ["forward_list", "list"],910 },911 {912 "name": "__cpp_lib_logical_traits",913 "values": {"c++17": 201510},914 "headers": ["type_traits"],915 },916 {917 "name": "__cpp_lib_make_from_tuple",918 "values": {"c++17": 201606},919 "headers": ["tuple"],920 },921 {922 "name": "__cpp_lib_make_reverse_iterator",923 "values": {"c++14": 201402},924 "headers": ["iterator"],925 },926 {927 "name": "__cpp_lib_make_unique",928 "values": {"c++14": 201304},929 "headers": ["memory"],930 },931 {932 "name": "__cpp_lib_map_try_emplace",933 "values": {"c++17": 201411},934 "headers": ["map"],935 },936 {937 "name": "__cpp_lib_math_constants",938 "values": {"c++20": 201907},939 "headers": ["numbers"],940 },941 {942 "name": "__cpp_lib_math_special_functions",943 "values": {"c++17": 201603},944 "headers": ["cmath"],945 "unimplemented": True,946 },947 {948 "name": "__cpp_lib_mdspan",949 "values": {950 "c++23": 202207,951 "c++26": 202406, # P2389R2 dextents Index Type Parameter952 },953 "headers": ["mdspan"],954 },955 {956 "name": "__cpp_lib_memory_resource",957 "values": {"c++17": 201603},958 "headers": ["memory_resource"],959 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR",960 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR",961 },962 {963 "name": "__cpp_lib_modules",964 "values": {"c++23": 202207},965 "headers": [],966 },967 {968 "name": "__cpp_lib_move_iterator_concept",969 "values": {"c++20": 202207},970 "headers": ["iterator"],971 },972 {973 "name": "__cpp_lib_move_only_function",974 "values": {"c++23": 202110},975 "headers": ["functional"],976 "unimplemented": True,977 },978 {979 "name": "__cpp_lib_node_extract",980 "values": {"c++17": 201606},981 "headers": ["map", "set", "unordered_map", "unordered_set"],982 },983 {984 "name": "__cpp_lib_nonmember_container_access",985 "values": {"c++17": 201411},986 "headers": [987 "array",988 "deque",989 "forward_list",990 "iterator",991 "list",992 "map",993 "regex",994 "set",995 "string",996 "unordered_map",997 "unordered_set",998 "vector",999 ],1000 },1001 {1002 "name": "__cpp_lib_not_fn",1003 "values": {1004 "c++17": 201603,1005 "c++26": 202306, # P2714R1 Bind front and back to NTTP callables1006 },1007 "headers": ["functional"],1008 },1009 {1010 "name": "__cpp_lib_null_iterators",1011 "values": {"c++14": 201304},1012 "headers": ["iterator"],1013 },1014 {1015 "name": "__cpp_lib_optional",1016 "values": {1017 "c++17": 201606,1018 "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant1019 "c++23": 202110, # P0798R8 Monadic operations for std::optional + LWG3621 Remove feature-test macro __cpp_lib_monadic_optional1020 "c++26": 202506, # P2988R12: std::optional<T&>1021 },1022 "headers": ["optional"],1023 },1024 {1025 "name": "__cpp_lib_optional_range_support",1026 "values": {"c++26": 202406}, # P3168R2 Give std::optional Range Support1027 "headers": ["optional"],1028 },1029 {1030 "name": "__cpp_lib_out_ptr",1031 "values": {1032 "c++23": 202106,1033 "c++26": 202311, # P2833R2 Freestanding Library: inout expected span1034 },1035 "headers": ["memory"],1036 },1037 {1038 "name": "__cpp_lib_parallel_algorithm",1039 "values": {"c++17": 201603},1040 "headers": ["algorithm", "numeric"],1041 "unimplemented": True,1042 },1043 {1044 "name": "__cpp_lib_philox_engine",1045 "values": {1046 "c++26": 2024061047 }, # P2075R6 Philox as an extension of the C++ RNG engines1048 # Note the paper mentions 202310L as value, which differs from the typical procedure.1049 "headers": ["random"],1050 "unimplemented": True,1051 },1052 {1053 "name": "__cpp_lib_polymorphic_allocator",1054 "values": {"c++20": 201902},1055 "headers": ["memory_resource"],1056 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR",1057 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR",1058 },1059 {1060 "name": "__cpp_lib_print",1061 "values": {1062 "c++23": 202207,1063 # "c++26": 202403, # P3107R5: Permit an efficient implementation of std::print1064 # "c++26": 202406, # P3235R3 std::print more types faster with less memory1065 },1066 "headers": ["ostream", "print"],1067 # Trying to use `std::print` where to_chars floating-point is not1068 # available causes compilation errors, even with non floating-point types.1069 # https://llvm.org/PR1253531070 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT",1071 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT",1072 },1073 {1074 "name": "__cpp_lib_quoted_string_io",1075 "values": {"c++14": 201304},1076 "headers": ["iomanip"],1077 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_LOCALIZATION",1078 "libcxx_guard": "_LIBCPP_HAS_LOCALIZATION",1079 },1080 {1081 "name": "__cpp_lib_ranges",1082 "values": {1083 "c++20": 202110, # P2415R2 What is a view?1084 "c++23": 202406, # P2997R1 Removing the common reference requirement from the indirectly invocable concepts (implemented as a DR against C++20)1085 },1086 "headers": ["algorithm", "functional", "iterator", "memory", "ranges"],1087 },1088 {1089 "name": "__cpp_lib_ranges_as_const",1090 "values": {1091 "c++23": 202207 # P2278R4 cbegin should always return a constant iterator1092 # 202311 # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility1093 },1094 "headers": ["ranges"],1095 "unimplemented": True,1096 },1097 {1098 "name": "__cpp_lib_ranges_as_rvalue",1099 "values": {"c++23": 202207},1100 "headers": ["ranges"],1101 },1102 {1103 "name": "__cpp_lib_ranges_chunk",1104 "values": {"c++23": 202202},1105 "headers": ["ranges"],1106 "unimplemented": True,1107 },1108 {1109 "name": "__cpp_lib_ranges_chunk_by",1110 "values": {"c++23": 202202},1111 "headers": ["ranges"],1112 },1113 {1114 "name": "__cpp_lib_ranges_concat",1115 "values": {"c++26": 202403}, # P2542R8: views::concat1116 "headers": ["ranges"],1117 "unimplemented": True,1118 },1119 {1120 "name": "__cpp_lib_ranges_contains",1121 "values": {"c++23": 202207},1122 "headers": ["algorithm"],1123 },1124 {1125 "name": "__cpp_lib_ranges_find_last",1126 "values": {"c++23": 202207},1127 "headers": ["algorithm"],1128 },1129 {1130 "name": "__cpp_lib_ranges_indices",1131 "values": {"c++26": 202506},1132 "headers": ["ranges"],1133 },1134 {1135 "name": "__cpp_lib_ranges_iota",1136 "values": {"c++23": 202202},1137 "headers": ["numeric"],1138 },1139 {1140 "name": "__cpp_lib_ranges_join_with",1141 "values": {"c++23": 202202},1142 "headers": ["ranges"],1143 },1144 {1145 "name": "__cpp_lib_ranges_repeat",1146 "values": {"c++23": 202207},1147 "headers": ["ranges"],1148 },1149 {1150 "name": "__cpp_lib_ranges_slide",1151 "values": {"c++23": 202202},1152 "headers": ["ranges"],1153 "unimplemented": True,1154 },1155 {1156 "name": "__cpp_lib_ranges_starts_ends_with",1157 "values": {"c++23": 202106},1158 "headers": ["algorithm"],1159 },1160 {1161 "name": "__cpp_lib_ranges_to_container",1162 "values": {"c++23": 202202},1163 "headers": ["ranges"],1164 },1165 {1166 "name": "__cpp_lib_ranges_zip",1167 "values": {"c++23": 202110},1168 "headers": ["ranges", "tuple", "utility"],1169 "unimplemented": True,1170 },1171 {1172 "name": "__cpp_lib_ratio",1173 "values": {"c++26": 202306}, # P2734R0 Adding the new SI prefixes1174 "headers": ["ratio"],1175 },1176 {1177 "name": "__cpp_lib_raw_memory_algorithms",1178 "values": {"c++17": 201606},1179 "headers": ["memory"],1180 },1181 {1182 "name": "__cpp_lib_rcu",1183 "values": {"c++26": 202306}, # P2545R4 Read-Copy Update (RCU)1184 "headers": [1185 "rcu" # TODO verify this entry since the paper was underspecified.1186 ],1187 "unimplemented": True,1188 },1189 {1190 "name": "__cpp_lib_reference_from_temporary",1191 "values": {"c++23": 202202},1192 "headers": ["type_traits"],1193 "unimplemented": True,1194 },1195 {1196 "name": "__cpp_lib_reference_wrapper",1197 "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper1198 "headers": ["functional"],1199 },1200 {1201 "name": "__cpp_lib_remove_cvref",1202 "values": {"c++20": 201711},1203 "headers": ["type_traits"],1204 },1205 {1206 "name": "__cpp_lib_result_of_sfinae",1207 "values": {"c++14": 201210},1208 "headers": ["functional", "type_traits"],1209 },1210 {1211 "name": "__cpp_lib_robust_nonmodifying_seq_ops",1212 "values": {"c++14": 201304},1213 "headers": ["algorithm"],1214 },1215 {1216 "name": "__cpp_lib_sample",1217 "values": {"c++17": 201603},1218 "headers": ["algorithm"],1219 },1220 {1221 "name": "__cpp_lib_saturation_arithmetic",1222 "values": {"c++26": 202311}, # P0543R3 Saturation arithmetic1223 "headers": ["numeric"],1224 },1225 {1226 "name": "__cpp_lib_scoped_lock",1227 "values": {"c++17": 201703},1228 "headers": ["mutex"],1229 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",1230 "libcxx_guard": "_LIBCPP_HAS_THREADS",1231 },1232 {1233 "name": "__cpp_lib_semaphore",1234 "values": {"c++20": 201907},1235 "headers": ["semaphore"],1236 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",1237 "libcxx_guard": "_LIBCPP_HAS_THREADS",1238 },1239 {1240 "name": "__cpp_lib_senders",1241 "values": {"c++26": 202406}, # P2300R10 std::execution1242 "headers": ["execution"],1243 "unimplemented": True,1244 },1245 {1246 "name": "__cpp_lib_shared_mutex",1247 "values": {"c++17": 201505},1248 "headers": ["shared_mutex"],1249 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",1250 "libcxx_guard": "_LIBCPP_HAS_THREADS",1251 },1252 {1253 "name": "__cpp_lib_shared_ptr_arrays",1254 "values": {"c++17": 201611, "c++20": 201707},1255 "headers": ["memory"],1256 },1257 {1258 "name": "__cpp_lib_shared_ptr_weak_type",1259 "values": {"c++17": 201606},1260 "headers": ["memory"],1261 },1262 {1263 "name": "__cpp_lib_shared_timed_mutex",1264 "values": {"c++14": 201402},1265 "headers": ["shared_mutex"],1266 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",1267 "libcxx_guard": "_LIBCPP_HAS_THREADS",1268 },1269 {1270 "name": "__cpp_lib_shift",1271 "values": {"c++20": 201806},1272 "headers": ["algorithm"],1273 },1274 {1275 "name": "__cpp_lib_smart_ptr_for_overwrite",1276 "values": {"c++20": 202002},1277 "headers": ["memory"],1278 },1279 {1280 "name": "__cpp_lib_smart_ptr_owner_equality",1281 "values": {1282 "c++26": 202306 # P1901R2 Enabling the Use of weak_ptr as Keys in Unordered Associative Containers1283 },1284 "headers": ["memory"],1285 "unimplemented": True,1286 },1287 {1288 "name": "__cpp_lib_source_location",1289 "values": {"c++20": 201907},1290 "headers": ["source_location"],1291 },1292 {1293 "name": "__cpp_lib_span",1294 "values": {1295 "c++20": 202002,1296 # "c++26": 202311, # P2821R5 span.at()1297 # 202311 # P2833R2 Freestanding Library: inout expected span1298 },1299 "headers": ["span"],1300 },1301 {1302 "name": "__cpp_lib_span_at",1303 "values": {"c++26": 202311}, # P2821R3 span.at()1304 "headers": ["span"],1305 },1306 {1307 "name": "__cpp_lib_span_initializer_list",1308 "values": {"c++26": 202311}, # P2447R6 std::span over an initializer list1309 "headers": ["span"],1310 },1311 {1312 "name": "__cpp_lib_spanstream",1313 "values": {"c++23": 202106},1314 "headers": ["spanstream"],1315 "unimplemented": True,1316 },1317 {1318 "name": "__cpp_lib_ssize",1319 "values": {"c++20": 201902},1320 "headers": ["iterator"],1321 },1322 {1323 "name": "__cpp_lib_sstream_from_string_view",1324 "values": {1325 "c++26": 202306 # P2495R3 Interfacing stringstreams with string_view1326 },1327 "headers": ["sstream"],1328 },1329 {1330 "name": "__cpp_lib_stacktrace",1331 "values": {"c++23": 202011},1332 "headers": ["stacktrace"],1333 "unimplemented": True,1334 },1335 {1336 "name": "__cpp_lib_starts_ends_with",1337 "values": {"c++20": 201711},1338 "headers": ["string", "string_view"],1339 },1340 {1341 "name": "__cpp_lib_stdatomic_h",1342 "values": {"c++23": 202011},1343 "headers": ["stdatomic.h"],1344 },1345 {1346 "name": "__cpp_lib_string_contains",1347 "values": {"c++23": 202011},1348 "headers": ["string", "string_view"],1349 },1350 {1351 "name": "__cpp_lib_string_resize_and_overwrite",1352 "values": {"c++23": 202110},1353 "headers": ["string"],1354 },1355 {1356 "name": "__cpp_lib_string_subview",1357 "values": {"c++26": 202506},1358 "headers": ["string", "string_view"],1359 },1360 {1361 "name": "__cpp_lib_string_udls",1362 "values": {"c++14": 201304},1363 "headers": ["string"],1364 },1365 {1366 "name": "__cpp_lib_string_view",1367 "values": {1368 "c++17": 201606,1369 "c++20": 201803,1370 "c++26": 202403, # P2591R5: Concatenation of strings and string views1371 },1372 "headers": ["string", "string_view"],1373 },1374 {1375 "name": "__cpp_lib_submdspan",1376 "values": {1377 "c++26": 202306, # P2630R4: submdspan1378 # "c++26": 202403, # P2642R6: Padded mdspan layouts1379 },1380 "headers": ["mdspan"],1381 "unimplemented": True,1382 },1383 {1384 "name": "__cpp_lib_syncbuf",1385 "values": {"c++20": 201803},1386 "headers": ["syncstream"],1387 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM",1388 "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM",1389 },1390 {1391 "name": "__cpp_lib_text_encoding",1392 "values": {1393 "c++26": 202306 # P1885R12 Naming Text Encodings to Demystify Them1394 },1395 "headers": ["text_encoding"],1396 "unimplemented": True,1397 },1398 {1399 "name": "__cpp_lib_three_way_comparison",1400 "values": {"c++20": 201907},1401 "headers": ["compare"],1402 },1403 {1404 "name": "__cpp_lib_to_address",1405 "values": {"c++20": 201711},1406 "headers": ["memory"],1407 },1408 {1409 "name": "__cpp_lib_to_array",1410 "values": {"c++20": 201907},1411 "headers": ["array"],1412 },1413 {1414 "name": "__cpp_lib_to_chars",1415 "values": {1416 "c++17": 201611,1417 "c++26": 202306, # P2497R0 Testing for success or failure of <charconv> functions1418 },1419 "headers": ["charconv"],1420 "unimplemented": True,1421 },1422 {1423 "name": "__cpp_lib_to_string",1424 "values": {"c++26": 202306}, # P2587R3 to_string or not to_string1425 "headers": ["string"],1426 "unimplemented": True,1427 },1428 {1429 "name": "__cpp_lib_to_underlying",1430 "values": {"c++23": 202102},1431 "headers": ["utility"],1432 },1433 {1434 "name": "__cpp_lib_transformation_trait_aliases",1435 "values": {"c++14": 201304},1436 "headers": ["type_traits"],1437 },1438 {1439 "name": "__cpp_lib_transparent_operators",1440 "values": {"c++14": 201210, "c++17": 201510},1441 "headers": ["functional", "memory"],1442 },1443 {1444 "name": "__cpp_lib_tuple_element_t",1445 "values": {"c++14": 201402},1446 "headers": ["tuple"],1447 },1448 {1449 "name": "__cpp_lib_tuple_like",1450 "values": {1451 "c++23": 202207, # P2165R4 Compatibility between tuple, pair and tuple-like objects1452 "c++26": 202311, # P2819R2 Add tuple protocol to complex (implemented)1453 },1454 "headers": ["map", "tuple", "unordered_map", "utility"],1455 "unimplemented": True,1456 },1457 {1458 "name": "__cpp_lib_tuples_by_type",1459 "values": {"c++14": 201304},1460 "headers": ["tuple", "utility"],1461 },1462 {1463 "name": "__cpp_lib_type_identity",1464 "values": {"c++20": 201806},1465 "headers": ["type_traits"],1466 },1467 {1468 "name": "__cpp_lib_type_trait_variable_templates",1469 "values": {"c++17": 201510},1470 "headers": ["type_traits"],1471 },1472 {1473 "name": "__cpp_lib_uncaught_exceptions",1474 "values": {"c++17": 201411},1475 "headers": ["exception"],1476 },1477 {1478 "name": "__cpp_lib_unordered_map_try_emplace",1479 "values": {"c++17": 201411},1480 "headers": ["unordered_map"],1481 },1482 {1483 "name": "__cpp_lib_unreachable",1484 "values": {"c++23": 202202},1485 "headers": ["utility"],1486 },1487 {1488 "name": "__cpp_lib_unwrap_ref",1489 "values": {"c++20": 201811},1490 "headers": ["functional"],1491 },1492 {1493 "name": "__cpp_lib_variant",1494 "values": {1495 "c++17": 202102, # std::visit for classes derived from std::variant1496 "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant1497 "c++26": 202306, # P2637R3 Member visit1498 },1499 "headers": ["variant"],1500 },1501 {1502 "name": "__cpp_lib_void_t",1503 "values": {"c++17": 201411},1504 "headers": ["type_traits"],1505 },1506 ]1507]1508 1509assert feature_test_macros == sorted(feature_test_macros, key=lambda tc: tc["name"])1510for tc in feature_test_macros:1511 assert tc["headers"] == sorted(tc["headers"]), tc1512 assert ("libcxx_guard" in tc) == ("test_suite_guard" in tc), tc1513 valid_keys = ["name", "values", "headers", "libcxx_guard", "test_suite_guard", "unimplemented"]1514 assert all(key in valid_keys for key in tc.keys()), tc1515 1516# Map from each header to the Lit annotations that should be used for1517# tests that include that header.1518#1519# For example, when threads are not supported, any test that includes1520# <thread> should be marked as UNSUPPORTED, because including <thread>1521# is a hard error in that case.1522lit_markup = {1523 "barrier": ["UNSUPPORTED: no-threads"],1524 "filesystem": ["UNSUPPORTED: no-filesystem"],1525 "fstream": ["UNSUPPORTED: no-localization"],1526 "iomanip": ["UNSUPPORTED: no-localization"],1527 "ios": ["UNSUPPORTED: no-localization"],1528 "iostream": ["UNSUPPORTED: no-localization"],1529 "istream": ["UNSUPPORTED: no-localization"],1530 "latch": ["UNSUPPORTED: no-threads"],1531 "locale": ["UNSUPPORTED: no-localization"],1532 "mutex": ["UNSUPPORTED: no-threads"],1533 "ostream": ["UNSUPPORTED: no-localization"],1534 "print": ["UNSUPPORTED: no-filesystem"],1535 "regex": ["UNSUPPORTED: no-localization"],1536 "semaphore": ["UNSUPPORTED: no-threads"],1537 "shared_mutex": ["UNSUPPORTED: no-threads"],1538 "sstream": ["UNSUPPORTED: no-localization"],1539 "syncstream": ["UNSUPPORTED: no-localization"],1540 "stdatomic.h": ["UNSUPPORTED: no-threads"],1541 "stop_token": ["UNSUPPORTED: no-threads"],1542 "thread": ["UNSUPPORTED: no-threads"],1543}1544 1545 1546def get_std_dialects():1547 std_dialects = ["c++14", "c++17", "c++20", "c++23", "c++26"]1548 return list(std_dialects)1549 1550 1551def get_first_std(d):1552 for s in get_std_dialects():1553 if s in d.keys():1554 return s1555 return None1556 1557 1558def get_last_std(d):1559 rev_dialects = get_std_dialects()1560 rev_dialects.reverse()1561 for s in rev_dialects:1562 if s in d.keys():1563 return s1564 return None1565 1566 1567def get_std_before(d, std):1568 std_dialects = get_std_dialects()1569 candidates = std_dialects[0 : std_dialects.index(std)]1570 candidates.reverse()1571 for cand in candidates:1572 if cand in d.keys():1573 return cand1574 return None1575 1576 1577def get_value_before(d, std):1578 new_std = get_std_before(d, std)1579 if new_std is None:1580 return None1581 return d[new_std]1582 1583 1584def get_for_std(d, std):1585 # This catches the C++11 case for which there should be no defined feature1586 # test macros.1587 std_dialects = get_std_dialects()1588 if std not in std_dialects:1589 return None1590 # Find the value for the newest C++ dialect between C++14 and std1591 std_list = list(std_dialects[0 : std_dialects.index(std) + 1])1592 std_list.reverse()1593 for s in std_list:1594 if s in d.keys():1595 return d[s]1596 return None1597 1598 1599def get_std_number(std):1600 return std.replace("c++", "")1601 1602 1603"""1604 Functions to produce the <version> header1605"""1606 1607 1608def produce_macros_definition_for_std(std):1609 result = ""1610 indent = 551611 for tc in feature_test_macros:1612 if std not in tc["values"]:1613 continue1614 inner_indent = 11615 if "test_suite_guard" in tc.keys():1616 result += "# if %s\n" % tc["libcxx_guard"]1617 inner_indent += 21618 if get_value_before(tc["values"], std) is not None:1619 assert "test_suite_guard" not in tc.keys()1620 result += "# undef %s\n" % tc["name"]1621 line = "#%sdefine %s" % ((" " * inner_indent), tc["name"])1622 line += " " * (indent - len(line))1623 line += " %sL" % tc["values"][std]1624 if "unimplemented" in tc.keys():1625 line = "// " + line1626 result += line1627 result += "\n"1628 if "test_suite_guard" in tc.keys():1629 result += "# endif\n"1630 return result.strip()1631 1632 1633def produce_macros_definitions():1634 macro_definition_template = """#if _LIBCPP_STD_VER >= {std_number}1635{macro_definition}1636#endif"""1637 1638 macros_definitions = []1639 for std in get_std_dialects():1640 macros_definitions.append(1641 macro_definition_template.format(1642 std_number=get_std_number(std),1643 macro_definition=produce_macros_definition_for_std(std),1644 )1645 )1646 1647 return "\n\n".join(macros_definitions)1648 1649 1650def chunks(l, n):1651 """Yield successive n-sized chunks from l."""1652 for i in range(0, len(l), n):1653 yield l[i : i + n]1654 1655 1656def produce_version_synopsis():1657 indent = 561658 header_indent = 56 + len("20XXYYL ")1659 result = ""1660 1661 def indent_to(s, val):1662 if len(s) >= val:1663 return s1664 s += " " * (val - len(s))1665 return s1666 1667 line = indent_to("Macro name", indent) + "Value"1668 line = indent_to(line, header_indent) + "Headers"1669 result += line + "\n"1670 for tc in feature_test_macros:1671 prev_defined_std = get_last_std(tc["values"])1672 line = "{name: <{indent}}{value}L ".format(1673 name=tc["name"], indent=indent, value=tc["values"][prev_defined_std]1674 )1675 headers = list(tc["headers"])1676 headers.remove("version")1677 for chunk in chunks(headers, 3):1678 line = indent_to(line, header_indent)1679 chunk = ["<%s>" % header for header in chunk]1680 line += " ".join(chunk)1681 result += line1682 result += "\n"1683 line = ""1684 while True:1685 prev_defined_std = get_std_before(tc["values"], prev_defined_std)1686 if prev_defined_std is None:1687 break1688 result += "%s%sL // %s\n" % (1689 indent_to("", indent),1690 tc["values"][prev_defined_std],1691 prev_defined_std.replace("c++", "C++"),1692 )1693 return result1694 1695 1696def produce_version_header():1697 template = """// -*- C++ -*-1698//===----------------------------------------------------------------------===//1699//1700// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.1701// See https://llvm.org/LICENSE.txt for license information.1702// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception1703//1704//===----------------------------------------------------------------------===//1705 1706#ifndef _LIBCPP_VERSIONH1707#define _LIBCPP_VERSIONH1708 1709/*1710 version synopsis1711 1712{synopsis}1713 1714*/1715 1716#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)1717# include <__cxx03/version>1718#else1719# include <__config>1720 1721# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)1722# pragma GCC system_header1723# endif1724 1725// clang-format off1726 1727{cxx_macros}1728 1729#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)1730 1731// clang-format on1732 1733#endif // _LIBCPP_VERSIONH1734"""1735 1736 version_str = template.format(1737 synopsis=produce_version_synopsis().strip(),1738 cxx_macros=produce_macros_definitions(),1739 )1740 version_header_path = os.path.join(include_path, "version")1741 with open(version_header_path, "w", newline="\n") as f:1742 f.write(version_str)1743 1744 1745"""1746 Functions to produce test files1747"""1748 1749test_types = {1750 "undefined": """1751# ifdef {name}1752# error "{name} should not be defined before {std_first}"1753# endif1754""",1755 "test_suite_guard": """1756# if {test_suite_guard}1757# ifndef {name}1758# error "{name} should be defined in {std}"1759# endif1760# if {name} != {value}1761# error "{name} should have the value {value} in {std}"1762# endif1763# else1764# ifdef {name}1765# error "{name} should not be defined when the requirement '{test_suite_guard}' is not met!"1766# endif1767# endif1768""",1769 "unimplemented": """1770# if !defined(_LIBCPP_VERSION)1771# ifndef {name}1772# error "{name} should be defined in {std}"1773# endif1774# if {name} != {value}1775# error "{name} should have the value {value} in {std}"1776# endif1777# else1778# ifdef {name}1779# error "{name} should not be defined because it is unimplemented in libc++!"1780# endif1781# endif1782""",1783 "defined": """1784# ifndef {name}1785# error "{name} should be defined in {std}"1786# endif1787# if {name} != {value}1788# error "{name} should have the value {value} in {std}"1789# endif1790""",1791}1792 1793 1794def generate_std_test(test_list, std):1795 result = ""1796 for tc in test_list:1797 val = get_for_std(tc["values"], std)1798 if val is not None:1799 val = "%sL" % val1800 if val is None:1801 result += test_types["undefined"].format(1802 name=tc["name"], std_first=get_first_std(tc["values"])1803 )1804 elif "unimplemented" in tc.keys():1805 result += test_types["unimplemented"].format(1806 name=tc["name"], value=val, std=std1807 )1808 elif "test_suite_guard" in tc.keys():1809 result += test_types["test_suite_guard"].format(1810 name=tc["name"],1811 value=val,1812 std=std,1813 test_suite_guard=tc["test_suite_guard"],1814 )1815 else:1816 result += test_types["defined"].format(name=tc["name"], value=val, std=std)1817 return result.strip()1818 1819 1820def generate_std_tests(test_list):1821 std_tests_template = """#if TEST_STD_VER < {first_std_number}1822 1823{pre_std_test}1824 1825{other_std_tests}1826 1827#elif TEST_STD_VER > {penultimate_std_number}1828 1829{last_std_test}1830 1831#endif // TEST_STD_VER > {penultimate_std_number}"""1832 1833 std_dialects = get_std_dialects()1834 1835 other_std_tests = []1836 for std in std_dialects[:-1]:1837 other_std_tests.append("#elif TEST_STD_VER == " + get_std_number(std))1838 other_std_tests.append(generate_std_test(test_list, std))1839 1840 std_tests = std_tests_template.format(1841 first_std_number=get_std_number(std_dialects[0]),1842 pre_std_test=generate_std_test(test_list, "c++11"),1843 other_std_tests="\n\n".join(other_std_tests),1844 penultimate_std_number=get_std_number(std_dialects[-2]),1845 last_std_test=generate_std_test(test_list, std_dialects[-1]),1846 )1847 1848 return std_tests1849 1850 1851def generate_synopsis(test_list):1852 max_name_len = max([len(tc["name"]) for tc in test_list])1853 indent = max_name_len + 81854 1855 def mk_line(prefix, suffix):1856 return "{prefix: <{max_len}}{suffix}\n".format(1857 prefix=prefix, suffix=suffix, max_len=indent1858 )1859 1860 result = ""1861 result += mk_line("/* Constant", "Value")1862 for tc in test_list:1863 prefix = " %s" % tc["name"]1864 for std in [s for s in get_std_dialects() if s in tc["values"].keys()]:1865 result += mk_line(1866 prefix, "%sL [%s]" % (tc["values"][std], std.replace("c++", "C++"))1867 )1868 prefix = ""1869 result += "*/"1870 return result1871 1872 1873def produce_tests():1874 headers = set([h for tc in feature_test_macros for h in tc["headers"]])1875 for h in headers:1876 test_list = [tc for tc in feature_test_macros if h in tc["headers"]]1877 if not has_header(h):1878 for tc in test_list:1879 assert "unimplemented" in tc.keys()1880 continue1881 markup = "\n".join("// " + tag for tag in lit_markup.get(h, []))1882 test_body = """//===----------------------------------------------------------------------===//1883//1884// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.1885// See https://llvm.org/LICENSE.txt for license information.1886// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception1887//1888//===----------------------------------------------------------------------===//1889 1890// WARNING: This test was generated by {script_name}1891// and should not be edited manually.1892{markup}1893// <{header}>1894 1895// Test the feature test macros defined by <{header}>1896 1897// clang-format off1898 1899#include <{header}>1900#include "test_macros.h"1901 1902{cxx_tests}1903 1904// clang-format on1905""".format(1906 script_name=script_name,1907 header=h,1908 markup=("\n{}\n".format(markup) if markup else ""),1909 cxx_tests=generate_std_tests(test_list),1910 )1911 test_name = "{header}.version.compile.pass.cpp".format(header=h)1912 out_path = os.path.join(macro_test_path, test_name)1913 with open(out_path, "w", newline="\n") as f:1914 f.write(test_body)1915 1916 1917"""1918 Produce documentation for the feature test macros1919"""1920 1921 1922def make_widths(grid):1923 widths = []1924 for i in range(0, len(grid[0])):1925 cell_width = 2 + max(1926 reduce(lambda x, y: x + y, [[len(row[i])] for row in grid], [])1927 )1928 widths += [cell_width]1929 return widths1930 1931 1932def create_table(grid, indent):1933 indent_str = " " * indent1934 col_widths = make_widths(grid)1935 result = [indent_str + add_divider(col_widths, 2)]1936 header_flag = 21937 for row_i in range(0, len(grid)):1938 row = grid[row_i]1939 line = indent_str + " ".join(1940 [pad_cell(row[i], col_widths[i]) for i in range(0, len(row))]1941 )1942 result.append(line.rstrip())1943 if row_i == len(grid) - 1:1944 header_flag = 21945 if row[0].startswith("**"):1946 header_flag += 11947 separator = indent_str + add_divider(col_widths, header_flag)1948 result.append(separator.rstrip())1949 header_flag = 01950 return "\n".join(result)1951 1952 1953def add_divider(widths, header_flag):1954 if header_flag == 3:1955 return "=".join(["=" * w for w in widths])1956 if header_flag == 2:1957 return " ".join(["=" * w for w in widths])1958 if header_flag == 1:1959 return "-".join(["-" * w for w in widths])1960 else:1961 return " ".join(["-" * w for w in widths])1962 1963 1964def pad_cell(s, length, left_align=True):1965 padding = (length - len(s)) * " "1966 return s + padding1967 1968 1969def get_status_table():1970 table = [["Macro Name", "Value"]]1971 for std in get_std_dialects():1972 table += [["**" + std.replace("c++", "C++") + "**", ""]]1973 for tc in feature_test_macros:1974 if std not in tc["values"].keys():1975 continue1976 value = "``%sL``" % tc["values"][std]1977 if "unimplemented" in tc.keys():1978 value = "*unimplemented*"1979 table += [["``%s``" % tc["name"], value]]1980 return table1981 1982 1983def produce_docs():1984 doc_str = """.. _FeatureTestMacroTable:1985 1986==========================1987Feature Test Macro Support1988==========================1989 1990.. contents::1991 :local:1992 1993Overview1994========1995 1996This file documents the feature test macros currently supported by libc++.1997 1998.. _feature-status:1999 2000Status2001======2002 2003.. table:: Current Status2004 :name: feature-status-table2005 :widths: auto2006 2007{status_tables}2008 2009""".format(2010 status_tables=create_table(get_status_table(), 4)2011 )2012 2013 table_doc_path = os.path.join(docs_path, "FeatureTestMacroTable.rst")2014 with open(table_doc_path, "w", newline="\n") as f:2015 f.write(doc_str)2016 2017 2018Std = NewType("Std", str) # Standard version number2019Ftm = NewType("Ftm", str) # The name of a feature test macro2020Value = NewType("Value", str) # The value of a feature test macro including the L suffix2021 2022@dataclass2023class Metadata:2024 headers: List[str] = None2025 available_since: Std = None2026 test_suite_guard: str = None2027 libcxx_guard: str = None2028 2029 2030@dataclass2031class VersionHeader:2032 value: Value = None2033 implemented: bool = None2034 need_undef: bool = None2035 condition: str = None2036 2037 2038@dataclass2039class FtmHeaderTest:2040 value: Value = None2041 implemented: bool = None2042 condition: str = None2043 2044def get_ftms(2045 data, std_dialects: List[Std], use_implemented_status: bool2046) -> Dict[Ftm, Dict[Std, Optional[Value]]]:2047 """Impementation for FeatureTestMacros.(standard|implemented)_ftms()."""2048 result = dict()2049 for feature in data:2050 last = None2051 entry = dict()2052 implemented = True2053 for std in std_dialects:2054 if std not in feature["values"].keys():2055 if last == None:2056 continue2057 else:2058 entry[std] = last2059 else:2060 if implemented:2061 values = feature["values"][std]2062 assert len(values) > 0, f"{feature['name']}[{std}] has no entries"2063 for value in values:2064 papers = list(values[value])2065 assert (2066 len(papers) > 02067 ), f"{feature['name']}[{std}][{value}] has no entries"2068 for paper in papers:2069 if use_implemented_status and not paper["implemented"]:2070 implemented = False2071 break2072 if implemented:2073 last = f"{value}L"2074 else:2075 break2076 2077 if last:2078 entry[std] = last2079 result[feature["name"]] = entry2080 2081 return result2082 2083 2084def generate_version_header_dialect_block(data: Dict[Ftm, VersionHeader]) -> str:2085 """Generates the contents of the version header for a dialect.2086 2087 This generates the contents of a2088 #if _LIBCPP_STD_VER >= XY2089 #endif // _LIBCPP_STD_VER >= XY2090 block.2091 """2092 result = ""2093 for element in data:2094 for ftm, entry in element.items():2095 if not entry.implemented:2096 # When a FTM is not implemented don't add the guards2097 # or undefine the (possibly) defined macro.2098 result += f"// define {ftm} {entry.value}\n"2099 else:2100 need_undef = entry.need_undef2101 if entry.condition:2102 result += f"# if {entry.condition}\n"2103 if entry.need_undef:2104 result += f"# undef {ftm}\n"2105 result += f"# define {ftm} {entry.value}\n"2106 result += f"# endif\n"2107 else:2108 if entry.need_undef:2109 result += f"# undef {ftm}\n"2110 result += f"# define {ftm} {entry.value}\n"2111 2112 return result2113 2114 2115def generate_version_header_implementation(2116 data: Dict[Std, Dict[Ftm, VersionHeader]]2117) -> str:2118 """Generates the body of the version header."""2119 2120 template = """#if _LIBCPP_STD_VER >= {dialect}2121{feature_test_macros}#endif // _LIBCPP_STD_VER >= {dialect}"""2122 2123 result = []2124 for std, ftms in data.items():2125 result.append(2126 template.format(2127 dialect=std,2128 feature_test_macros=generate_version_header_dialect_block(ftms),2129 )2130 )2131 2132 return "\n\n".join(result)2133 2134#2135# The templates used to create a FTM test file2136#2137 2138 2139ftm_header_test_file_contents = """//===----------------------------------------------------------------------===//2140//2141// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.2142// See https://llvm.org/LICENSE.txt for license information.2143// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception2144//2145//===----------------------------------------------------------------------===//2146 2147// WARNING: This test was generated by {script_name}2148// and should not be edited manually.2149 2150{lit_markup}// <{header}>2151 2152// Test the feature test macros defined by <{header}>2153 2154// clang-format off2155 2156{include}2157#include "test_macros.h"2158{data}2159 2160// clang-format on2161 2162"""2163 2164 2165ftm_header_test_file_include_unconditional = """\2166#include <{header}>\2167"""2168 2169# On Windows the Windows SDK is on the include path, that means the MSVC STL2170# headers can be found as well, tricking __has_include into thinking that2171# libc++ provides the header. This means the test is also not executed when2172# using this test with MSVC and MSVC STL.2173ftm_header_test_file_include_conditional = """\2174#if !defined(_WIN32) && __has_include(<{header}>)2175# include <{header}>2176#endif\2177"""2178 2179ftm_header_test_file_dialect_block = """2180#{pp_if} TEST_STD_VER {operator} {dialect}2181{tests}\2182"""2183 2184class FeatureTestMacros:2185 """Provides all feature-test macro (FTM) output components.2186 2187 The class has several generators to use the feature-test macros in libc++:2188 - FTM status page2189 - The version header and its tests2190 2191 This class is not intended to duplicate2192 https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#library-feature-test-macros2193 SD-FeatureTest: Feature-Test Macros and Policies2194 2195 Historically libc++ did not list all papers affecting a FTM, the new data2196 structure is able to do that. However there is no intention to add the2197 historical data. After papers have been implemented this information can be2198 removed. For example, __cpp_lib_format's value 201907 requires 3 papers,2199 once implemented it can be reduced to 1 paper and remove the paper number2200 and title. This would reduce the size of the data.2201 2202 The input data is stored in the following JSON format:2203 [ # A list with multiple feature-test macro entries.2204 {2205 # required2206 # The name of the feature test macro. These names should be unique and2207 # sorted in the list.2208 "name": "__cpp_lib_any",2209 2210 # required2211 # A map with the value of the FTM based on the language standard. Only2212 # the versions in which the value of the FTM changes are listed. For2213 # example, this macro's value does not change in C++20 so it does not2214 # list C++20. If it changes in C++26, it will have entries for C++17 and2215 # C++26.2216 "values": {2217 2218 # required2219 # The language standard, also named dialect in this class.2220 "c++17": {2221 2222 # required2223 # The value of the feature test macro. This contains an array with2224 # one or more papers that need to be implemented before this value2225 # is considered implemented.2226 "201606": [2227 {2228 # optional2229 # Contains the paper number that is part of the FTM version.2230 "number": "P0220R1",2231 2232 # optional2233 # Contains the title of the paper that is part of the FTM2234 # version.2235 "title": "Adopt Library Fundamentals V1 TS Components for C++17"2236 2237 # required2238 # The implementation status of the paper.2239 "implemented": true2240 }2241 ]2242 }2243 },2244 2245 # required2246 # A sorted list of headers that should provide the FTM. The header2247 # <version> is automatically added to this list. This list could be2248 # empty. For example, __cpp_lib_modules is only present in version.2249 # Requiring the field makes it easier to detect accidental omission.2250 "headers": [2251 "any"2252 ],2253 2254 # optional, required when libcxx_guard is present2255 # This field is used only to generate the unit tests for the2256 # feature-test macros. It can't depend on macros defined in <__config>2257 # because the `test/std/` parts of the test suite are intended to be2258 # portable to any C++ standard library implementation, not just libc++.2259 # It may depend on2260 # * macros defined by the compiler itself, or2261 # * macros generated by CMake.2262 # In some cases we add also depend on macros defined in2263 # <__availability>.2264 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR"2265 2266 # optional, required when test_suite_guard is present2267 # This field is used only to guard the feature-test macro in2268 # <version>. It may be the same as `test_suite_guard`, or it may2269 # depend on macros defined in <__config>.2270 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR"2271 },2272 ]2273 """2274 2275 # The JSON data structure.2276 __data = None2277 # The headers not available in libc++.2278 #2279 # This could be detected based on FTM status, however that gives some odd2280 # results. For example, at the moment __cpp_lib_constexpr_cmath is not2281 # implemented, which flags `<cstdlib>` as not implemented. The availability2282 # of headers is maintained for the C++ Standard Library modules.2283 __unavailable_headers = None2284 2285 def __init__(self, filename: str, unavailable_headers: List[str]):2286 """Initializes the class with the JSON data in the file 'filename'."""2287 with open(filename) as f:2288 self.__data = json.load(f)2289 2290 self.__unavailable_headers = set(unavailable_headers)2291 2292 @functools.cached_property2293 def std_dialects(self) -> List[Std]:2294 """Returns the C++ dialects avaiable.2295 2296 The available dialects are based on the 'c++xy' keys found the 'values'2297 entries in '__data'. So when WG21 starts to feature-test macros for a2298 future C++ Standard this dialect will automatically be available.2299 2300 The return value is a sorted list with the C++ dialects used. Since FTM2301 were added in C++14 the list will not contain C++03 or C++11.2302 """2303 dialects = set()2304 for feature in self.__data:2305 keys = feature["values"].keys()2306 assert len(keys) > 0, "'values' is empty"2307 dialects |= keys2308 2309 return sorted(list(dialects))2310 2311 @functools.cached_property2312 def standard_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:2313 """Returns the FTM versions per dialect in the Standard.2314 2315 This function does not use the 'implemented' flag. The output contains2316 the versions used in the Standard. When a FTM in libc++ is not2317 implemented according to the Standard to output may opt to show the2318 expected value.2319 """2320 return get_ftms(self.__data, self.std_dialects, False)2321 2322 @functools.cached_property2323 def standard_library_headers(self) -> Set[str]:2324 """Returns a list of headers that contain at least one FTM."""2325 2326 result = set()2327 for value in self.ftm_metadata.values():2328 for header in value.headers:2329 result.add(header)2330 2331 return list(result)2332 2333 @functools.cached_property2334 def implemented_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:2335 """Returns the FTM versions per dialect implemented in libc++.2336 2337 Unlike `get_std_dialect_versions` this function uses the 'implemented'2338 flag. This returns the actual implementation status in libc++.2339 """2340 2341 return get_ftms(self.__data, self.std_dialects, True)2342 2343 2344 def is_implemented(self, ftm: Ftm, std: Std) -> bool:2345 """Has the FTM `ftm` been implemented in the dialect `std`?"""2346 2347 # When a paper for C++20 has not been implemented in libc++, then there will be no2348 # FTM entry in implemented_ftms for C++23 and later. Similarly, a paper like <format>2349 # has no entry in standard_ftms for e.g. C++11.2350 if not std in self.implemented_ftms[ftm].keys() or not std in self.standard_ftms[ftm].keys():2351 return False2352 2353 return self.implemented_ftms[ftm][std] == self.standard_ftms[ftm][std]2354 2355 @functools.cached_property2356 def ftm_metadata(self) -> Dict[Ftm, Metadata]:2357 """Returns the metadata of the FTMs defined in the Standard.2358 2359 The metadata does not depend on the C++ dialect used.2360 """2361 result = dict()2362 for feature in self.__data:2363 result[feature["name"]] = Metadata(2364 feature["headers"],2365 list(feature["values"])[0],2366 feature.get("test_suite_guard", None),2367 feature.get("libcxx_guard", None),2368 )2369 2370 return result2371 2372 @property2373 def version_header_implementation(self) -> Dict[Std, Dict[Ftm, VersionHeader]]:2374 """Generates the body of the version header."""2375 result = dict()2376 for std in self.std_dialects:2377 result[get_std_number(std)] = list()2378 2379 for ftm, values in self.standard_ftms.items():2380 last_value = None2381 last_entry = None2382 for std, value in values.items():2383 # When a newer Standard does not change the value of the macro2384 # there is no need to redefine it with the same value.2385 if last_value and value == last_value:2386 continue2387 last_value = value2388 2389 implemented = self.is_implemented(ftm, std)2390 entry = VersionHeader(2391 value,2392 implemented,2393 last_entry is not None and last_entry.implemented and implemented,2394 self.ftm_metadata[ftm].libcxx_guard,2395 )2396 2397 last_entry = entry2398 result[get_std_number(std)].append(dict({ftm: entry}))2399 2400 return result2401 2402 @property2403 def version_header(self) -> str:2404 """Generates the version header."""2405 template = """// -*- C++ -*-2406//===----------------------------------------------------------------------===//2407//2408// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.2409// See https://llvm.org/LICENSE.txt for license information.2410// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception2411//2412//===----------------------------------------------------------------------===//2413 2414#ifndef _LIBCPP_VERSIONH2415#define _LIBCPP_VERSIONH2416 2417#include <__config>2418 2419#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)2420# pragma GCC system_header2421#endif2422 2423{feature_test_macros}2424 2425#endif // _LIBCPP_VERSIONH2426"""2427 return template.format(2428 feature_test_macros=generate_version_header_implementation(2429 self.version_header_implementation2430 )2431 )2432 2433 def header_ftm_data(self, header: str) -> Dict[Std, List[Dict[Ftm, FtmHeaderTest]]]:2434 """Generates the FTM information for a `header`."""2435 2436 result = dict()2437 for std in self.std_dialects:2438 result[get_std_number(std)] = list()2439 2440 for ftm, values in self.standard_ftms.items():2441 if not header in self.ftm_metadata[ftm].headers:2442 continue2443 2444 last_value = None2445 last_entry = None2446 2447 for std in self.std_dialects:2448 if not std in values.keys():2449 result[get_std_number(std)].append({ftm: None})2450 continue2451 2452 result[get_std_number(std)].append(2453 {2454 ftm: FtmHeaderTest(2455 values[std],2456 self.is_implemented(ftm, std),2457 self.ftm_metadata[ftm].test_suite_guard,2458 )2459 }2460 )2461 2462 return result2463 2464 2465 def generate_ftm_test(self, std: Std, ftm: Ftm, value: FtmHeaderTest) -> str:2466 """Adds a single `ftm` test for C++ `std` based on the status information in `value`.2467 2468 When std == None this test is generating the TEST_STD_VER < MIN. Where2469 MIN is the minimum version that has a FTM defined. (In the real data2470 this is 14, since FTM have been introduced in C++14.)2471 """2472 2473 ftm_unavailable_in_dialect = """2474# ifdef {ftm}2475# error "{ftm} should not be defined before {dialect}"2476# endif2477"""2478 2479 ftm_not_implemented = """2480# if !defined(_LIBCPP_VERSION)2481# ifndef {ftm}2482# error "{ftm} should be defined in {dialect}"2483# endif2484# if {ftm} != {value}2485# error "{ftm} should have the value {value} in {dialect}"2486# endif2487# else2488# ifdef {ftm}2489# error "{ftm} should not be defined because it is unimplemented in libc++!"2490# endif2491# endif2492"""2493 2494 ftm_conditionally_implemented = """2495# if {condition}2496# ifndef {ftm}2497# error "{ftm} should be defined in {dialect}"2498# endif2499# if {ftm} != {value}2500# error "{ftm} should have the value {value} in {dialect}"2501# endif2502# else2503# ifdef {ftm}2504# error "{ftm} should not be defined when the requirement '{condition}' is not met!"2505# endif2506# endif2507"""2508 2509 ftm_implemented = """2510# ifndef {ftm}2511# error "{ftm} should be defined in {dialect}"2512# endif2513# if {ftm} != {value}2514# error "{ftm} should have the value {value} in {dialect}"2515# endif2516"""2517 2518 if std == None or value == None:2519 return ftm_unavailable_in_dialect.format(2520 ftm=ftm, dialect=self.ftm_metadata[ftm].available_since2521 )2522 2523 if not value.implemented:2524 return ftm_not_implemented.format(2525 ftm=ftm, value=value.value, dialect=std2526 )2527 2528 if self.ftm_metadata[ftm].test_suite_guard:2529 return ftm_conditionally_implemented.format(2530 ftm=ftm,2531 value=value.value,2532 dialect=std,2533 condition=self.ftm_metadata[ftm].test_suite_guard,2534 )2535 2536 return ftm_implemented.format(ftm=ftm, value=value.value, dialect=std)2537 2538 def generate_header_test_dialect(2539 self, std: Std, data: List[Dict[Ftm, FtmHeaderTest]]2540 ) -> str:2541 """Returns the body a single `std` for the FTM test of a `header`."""2542 return "".join(2543 self.generate_ftm_test(std, ftm, value)2544 for element in data2545 for ftm, value in element.items()2546 )2547 2548 def generate_lit_markup(self, header:str) -> str:2549 if not header in lit_markup.keys():2550 return ""2551 2552 return "\n".join(f"// {markup}" for markup in lit_markup[header]) + "\n\n"2553 2554 def generate_header_test_file(self, header: str) -> str:2555 """Returns the body for the FTM test of a `header`."""2556 2557 # FTM block before the first Standard that introduced them.2558 # This test the macros are not available before this version.2559 data = ftm_header_test_file_dialect_block.format(2560 pp_if="if",2561 operator="<",2562 dialect=get_std_number(self.std_dialects[0]),2563 tests=self.generate_header_test_dialect(2564 None, next(iter(self.header_ftm_data(header).values()))2565 ),2566 )2567 2568 # FTM for all Standards that have FTM defined.2569 # Note in libc++ the TEST_STD_VER contains 99 for the Standard2570 # in development, therefore the last entry uses a different #elif.2571 data += "".join(2572 ftm_header_test_file_dialect_block.format(2573 pp_if="elif",2574 operator="==" if std != get_std_number(self.std_dialects[-1]) else ">",2575 dialect=std2576 if std != get_std_number(self.std_dialects[-1])2577 else get_std_number(self.std_dialects[-2]),2578 tests=self.generate_header_test_dialect(f"c++{std}", values),2579 )2580 for std, values in self.header_ftm_data(header).items()2581 )2582 2583 # The final #endif for the last #elif block.2584 data += f"\n#endif // TEST_STD_VER > {get_std_number(self.std_dialects[-2])}"2585 2586 # Generate the test for the requested header.2587 return ftm_header_test_file_contents.format(2588 script_name=script_name,2589 lit_markup=self.generate_lit_markup(header),2590 header=header,2591 include=(2592 ftm_header_test_file_include_conditional.format(header=header)2593 if header in self.__unavailable_headers2594 else ftm_header_test_file_include_unconditional.format(header=header)2595 ),2596 data=data2597 )2598 2599 def generate_header_test_directory(self, path: os.path) -> None:2600 """Generates all FTM tests in the directory `path`."""2601 2602 if not os.path.exists(path):2603 os.makedirs(path)2604 2605 for header in self.standard_library_headers:2606 with open(2607 os.path.join(path, f"{header}.version.compile.pass.cpp"),2608 "w",2609 newline="\n",2610 ) as f:2611 f.write(self.generate_header_test_file(header))2612 2613 2614def main():2615 produce_version_header()2616 produce_tests()2617 produce_docs()2618 2619 # Example how to use the new generator to generate the output.2620 if False:2621 ftm = FeatureTestMacros(2622 os.path.join(2623 source_root, "test", "libcxx", "feature_test_macro", "test_data.json"2624 ), headers_not_available2625 )2626 version_header_path = os.path.join(include_path, "version")2627 with open(version_header_path, "w", newline="\n") as f:2628 f.write(ftm.version_header)2629 2630 ftm.generate_header_test_directory(macro_test_path)2631 2632 2633if __name__ == "__main__":2634 main()2635