1#!/usr/bin/env python 2 3import os 4from builtins import range 5from functools import reduce 6from typing import Any, Dict, List # Needed for python 3.8 compatibility. 7import functools 8import json 9 10 11def 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", 27 ) 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") 31 ) 32 return script_name, src_root, include_path, docs_path, macro_test_path 33 34 35script_name, source_root, include_path, docs_path, macro_test_path = get_libcxx_paths() 36 37 38def has_header(h): 39 h_path = os.path.join(include_path, h) 40 return os.path.exists(h_path) 41 42 43def add_version_header(tc): 44 tc["headers"].append("version") 45 return tc 46 47 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# ================ ============================================================ 79feature_test_macros = [ 80 add_version_header(x) 81 for x in [ 82 { 83 "name": "__cpp_lib_adaptor_iterator_pair_constructor", 84 "values": {"c++23": 202106}, 85 "headers": ["queue", "stack"], 86 }, 87 { 88 "name": "__cpp_lib_addressof_constexpr", 89 "values": {"c++17": 201603}, 90 "headers": ["memory"], 91 }, 92 { 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 97 }, 98 "headers": ["memory"], 99 }, 100 { 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", 115 ], 116 }, 117 { 118 "name": "__cpp_lib_any", 119 "values": {"c++17": 201606}, 120 "headers": ["any"], 121 }, 122 { 123 "name": "__cpp_lib_apply", 124 "values": {"c++17": 201603}, 125 "headers": ["tuple"], 126 }, 127 { 128 "name": "__cpp_lib_array_constexpr", 129 "values": {"c++17": 201603, "c++20": 201811}, 130 "headers": ["array", "iterator"], 131 }, 132 { 133 "name": "__cpp_lib_as_const", 134 "values": {"c++17": 201510}, 135 "headers": ["utility"], 136 }, 137 { 138 "name": "__cpp_lib_associative_heterogeneous_erasure", 139 "values": {"c++23": 202110}, 140 "headers": ["map", "set", "unordered_map", "unordered_set"], 141 "unimplemented": True, 142 }, 143 { 144 "name": "__cpp_lib_associative_heterogeneous_insertion", 145 "values": { 146 "c++26": 202306 # P2363R5 Extending associative containers with the remaining heterogeneous overloads 147 }, 148 "headers": ["map", "set", "unordered_map", "unordered_set"], 149 "unimplemented": True, 150 }, 151 { 152 "name": "__cpp_lib_assume_aligned", 153 "values": {"c++20": 201811}, 154 "headers": ["memory"], 155 }, 156 { 157 "name": "__cpp_lib_atomic_flag_test", 158 "values": {"c++20": 201907}, 159 "headers": ["atomic"], 160 }, 161 { 162 "name": "__cpp_lib_atomic_float", 163 "values": {"c++20": 201711}, 164 "headers": ["atomic"], 165 "unimplemented": True, 166 }, 167 { 168 "name": "__cpp_lib_atomic_is_always_lock_free", 169 "values": {"c++17": 201603}, 170 "headers": ["atomic"], 171 }, 172 { 173 "name": "__cpp_lib_atomic_lock_free_type_aliases", 174 "values": {"c++20": 201907}, 175 "headers": ["atomic"], 176 }, 177 { 178 "name": "__cpp_lib_atomic_min_max", 179 "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum 180 "headers": ["atomic"], 181 "unimplemented": True, 182 }, 183 { 184 "name": "__cpp_lib_atomic_ref", 185 "values": {"c++20": 201806}, 186 "headers": ["atomic"], 187 }, 188 { 189 "name": "__cpp_lib_atomic_shared_ptr", 190 "values": {"c++20": 201711}, 191 "headers": ["atomic"], 192 "unimplemented": True, 193 }, 194 { 195 "name": "__cpp_lib_atomic_value_initialization", 196 "values": {"c++20": 201911}, 197 "headers": ["atomic", "memory"], 198 }, 199 { 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", 205 }, 206 { 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", 212 }, 213 { 214 "name": "__cpp_lib_bind_back", 215 "values": { 216 "c++23": 202202, 217 # "c++26": 202306, # P2714R1 Bind front and back to NTTP callables 218 }, 219 "headers": ["functional"], 220 }, 221 { 222 "name": "__cpp_lib_bind_front", 223 "values": { 224 "c++20": 201907, 225 "c++26": 202306, # P2714R1 Bind front and back to NTTP callables 226 }, 227 "headers": ["functional"], 228 }, 229 { 230 "name": "__cpp_lib_bit_cast", 231 "values": {"c++20": 201806}, 232 "headers": ["bit"], 233 }, 234 { 235 "name": "__cpp_lib_bitops", 236 "values": {"c++20": 201907}, 237 "headers": ["bit"], 238 }, 239 { 240 "name": "__cpp_lib_bitset", 241 "values": {"c++26": 202306}, # P2697R1 Interfacing bitset with string_view 242 "headers": ["bitset"], 243 }, 244 { 245 "name": "__cpp_lib_bool_constant", 246 "values": {"c++17": 201505}, 247 "headers": ["type_traits"], 248 }, 249 { 250 "name": "__cpp_lib_bounded_array_traits", 251 "values": {"c++20": 201902}, 252 "headers": ["type_traits"], 253 }, 254 { 255 "name": "__cpp_lib_boyer_moore_searcher", 256 "values": {"c++17": 201603}, 257 "headers": ["functional"], 258 }, 259 { 260 "name": "__cpp_lib_byte", 261 "values": {"c++17": 201603}, 262 "headers": ["cstddef"], 263 }, 264 { 265 "name": "__cpp_lib_byteswap", 266 "values": {"c++23": 202110}, 267 "headers": ["bit"], 268 }, 269 { 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", 281 ], 282 "test_suite_guard": "defined(__cpp_char8_t)", 283 "libcxx_guard": "_LIBCPP_HAS_CHAR8_T", 284 }, 285 { 286 "name": "__cpp_lib_chrono", 287 "values": { 288 "c++17": 201611, 289 # "c++26": 202306, # P2592R3 Hashing support for std::chrono value classes 290 }, 291 "headers": ["chrono"], 292 }, 293 { 294 "name": "__cpp_lib_chrono_udls", 295 "values": {"c++14": 201304}, 296 "headers": ["chrono"], 297 }, 298 { 299 "name": "__cpp_lib_clamp", 300 "values": {"c++17": 201603}, 301 "headers": ["algorithm"], 302 }, 303 { 304 "name": "__cpp_lib_complex_udls", 305 "values": {"c++14": 201309}, 306 "headers": ["complex"], 307 }, 308 { 309 "name": "__cpp_lib_concepts", 310 "values": {"c++20": 202002}, 311 "headers": ["concepts"], 312 }, 313 { 314 "name": "__cpp_lib_constexpr_algorithms", 315 "values": { 316 "c++20": 201806, 317 # "c++26": 202306, # P2562R1 constexpr Stable Sorting 318 }, 319 "headers": ["algorithm", "utility"], 320 }, 321 { 322 "name": "__cpp_lib_constexpr_bitset", 323 "values": {"c++23": 202207}, 324 "headers": ["bitset"], 325 }, 326 { 327 "name": "__cpp_lib_constexpr_charconv", 328 "values": {"c++23": 202207}, 329 "headers": ["charconv"], 330 }, 331 { 332 "name": "__cpp_lib_constexpr_cmath", 333 "values": {"c++23": 202202}, 334 "headers": ["cmath", "cstdlib"], 335 "unimplemented": True, 336 }, 337 { 338 "name": "__cpp_lib_constexpr_complex", 339 "values": {"c++20": 201711}, 340 "headers": ["complex"], 341 }, 342 { 343 "name": "__cpp_lib_constexpr_dynamic_alloc", 344 "values": {"c++20": 201907}, 345 "headers": ["memory"], 346 }, 347 { 348 "name": "__cpp_lib_constexpr_functional", 349 "values": {"c++20": 201907}, 350 "headers": ["functional"], 351 }, 352 { 353 "name": "__cpp_lib_constexpr_iterator", 354 "values": {"c++20": 201811}, 355 "headers": ["iterator"], 356 }, 357 { 358 "name": "__cpp_lib_constexpr_memory", 359 "values": {"c++20": 201811, "c++23": 202202}, 360 "headers": ["memory"], 361 }, 362 { 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)", 368 }, 369 { 370 "name": "__cpp_lib_constexpr_numeric", 371 "values": {"c++20": 201911}, 372 "headers": ["numeric"], 373 }, 374 { 375 "name": "__cpp_lib_constexpr_string", 376 "values": {"c++20": 201907}, 377 "headers": ["string"], 378 }, 379 { 380 "name": "__cpp_lib_constexpr_string_view", 381 "values": {"c++20": 201811}, 382 "headers": ["string_view"], 383 }, 384 { 385 "name": "__cpp_lib_constexpr_tuple", 386 "values": {"c++20": 201811}, 387 "headers": ["tuple"], 388 }, 389 { 390 "name": "__cpp_lib_constexpr_typeinfo", 391 "values": {"c++23": 202106}, 392 "headers": ["typeinfo"], 393 }, 394 { 395 "name": "__cpp_lib_constexpr_utility", 396 "values": {"c++20": 201811}, 397 "headers": ["utility"], 398 }, 399 { 400 "name": "__cpp_lib_constexpr_vector", 401 "values": {"c++20": 201907}, 402 "headers": ["vector"], 403 }, 404 { 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, 409 }, 410 { 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", 425 ], 426 }, 427 { 428 "name": "__cpp_lib_copyable_function", 429 "values": {"c++26": 202306}, # P2548R6 copyable_function 430 "headers": ["functional"], 431 "unimplemented": True, 432 }, 433 { 434 "name": "__cpp_lib_coroutine", 435 "values": {"c++20": 201902}, 436 "headers": ["coroutine"], 437 }, 438 { 439 "name": "__cpp_lib_debugging", 440 "values": { 441 "c++26": 202311, # P2546R5 Debugging Support 442 # "c++26": 202403, # P2810R4: is_debugger_present is_replaceable 443 }, 444 "headers": ["debugging"], 445 "unimplemented": True, 446 }, 447 { 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, 452 }, 453 { 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", 459 }, 460 { 461 "name": "__cpp_lib_enable_shared_from_this", 462 "values": {"c++17": 201603}, 463 "headers": ["memory"], 464 }, 465 { 466 "name": "__cpp_lib_endian", 467 "values": {"c++20": 201907}, 468 "headers": ["bit"], 469 }, 470 { 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", 483 ], 484 }, 485 { 486 "name": "__cpp_lib_exchange_function", 487 "values": {"c++14": 201304}, 488 "headers": ["utility"], 489 }, 490 { 491 "name": "__cpp_lib_execution", 492 "values": {"c++17": 201603, "c++20": 201902}, 493 "headers": ["execution"], 494 "unimplemented": True, 495 }, 496 { 497 "name": "__cpp_lib_expected", 498 "values": {"c++23": 202211}, 499 "headers": ["expected"], 500 }, 501 { 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", 507 }, 508 { 509 "name": "__cpp_lib_flat_map", 510 "values": {"c++23": 202207}, 511 "headers": ["flat_map"], 512 }, 513 { 514 "name": "__cpp_lib_flat_set", 515 "values": {"c++23": 202207}, 516 "headers": ["flat_set"], 517 "unimplemented": True, 518 }, 519 { 520 "name": "__cpp_lib_format", 521 "values": { 522 "c++20": 202110, 523 # "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types 524 # "c++26": 202306, P2637R3 Member Visit (implemented) 525 # "c++26": 202311, P2918R2 Runtime format strings II (implemented) 526 }, 527 # Note these three papers are adopted at the June 2023 meeting and have sequential numbering 528 # 202304 P2510R3 Formatting pointers (Implemented) 529 # 202305 P2757R3 Type-checking format args 530 # 202306 P2637R3 Member Visit 531 "headers": ["format"], 532 }, 533 { 534 "name": "__cpp_lib_format_path", 535 "values": {"c++26": 202403}, # P2845R8: Formatting of std::filesystem::path 536 "headers": ["filesystem"], 537 "unimplemented": True, 538 }, 539 { 540 "name": "__cpp_lib_format_ranges", 541 "values": {"c++23": 202207}, 542 "headers": ["format"], 543 }, 544 { 545 "name": "__cpp_lib_format_uchar", 546 "values": { 547 "c++20": 202311 # DR P2909R4 Fix formatting of code units as integers 548 }, 549 "headers": [ 550 "format" # TODO verify this entry since the paper was underspecified. 551 ], 552 }, 553 { 554 "name": "__cpp_lib_formatters", 555 "values": {"c++23": 202302}, 556 "headers": ["stacktrace", "thread"], 557 "unimplemented": True, 558 }, 559 { 560 "name": "__cpp_lib_forward_like", 561 "values": {"c++23": 202207}, 562 "headers": ["utility"], 563 }, 564 { 565 "name": "__cpp_lib_freestanding_algorithm", 566 "values": { 567 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes 568 }, 569 "headers": ["algorithm"], 570 "unimplemented": True, 571 }, 572 { 573 "name": "__cpp_lib_freestanding_array", 574 "values": { 575 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes 576 }, 577 "headers": ["array"], 578 "unimplemented": True, 579 }, 580 { 581 "name": "__cpp_lib_freestanding_cstring", 582 "values": { 583 "c++26": 202306 # P2338R4 Freestanding Library: Character primitives and the C library 584 # 202311 # P2407R5 Freestanding Library: Partial Classes 585 }, 586 "headers": ["cstring"], 587 "unimplemented": True, 588 }, 589 { 590 "name": "__cpp_lib_freestanding_expected", 591 "values": { 592 "c++26": 202311 # P2833R2 Freestanding Library: inout expected span 593 }, 594 "headers": ["expected"], 595 "unimplemented": True, 596 }, 597 { 598 "name": "__cpp_lib_freestanding_mdspan", 599 "values": { 600 "c++26": 202311 # P2833R2 Freestanding Library: inout expected span 601 }, 602 "headers": ["mdspan"], 603 "unimplemented": True, 604 }, 605 { 606 "name": "__cpp_lib_freestanding_optional", 607 "values": { 608 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes 609 }, 610 "headers": ["optional"], 611 "unimplemented": True, 612 }, 613 { 614 "name": "__cpp_lib_freestanding_string_view", 615 "values": { 616 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes 617 }, 618 "headers": ["string_view"], 619 "unimplemented": True, 620 }, 621 { 622 "name": "__cpp_lib_freestanding_variant", 623 "values": { 624 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes 625 }, 626 "headers": ["variant"], 627 "unimplemented": True, 628 }, 629 { 630 "name": "__cpp_lib_fstream_native_handle", 631 "values": {"c++26": 202306}, # P1759R6 Native handles and file streams 632 "headers": ["fstream"], 633 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION)", 634 "libcxx_guard": "_LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION", 635 }, 636 { 637 "name": "__cpp_lib_function_ref", 638 "values": { 639 "c++26": 202306 # P0792R14 function_ref: a type-erased callable reference 640 }, 641 "headers": ["functional"], 642 "unimplemented": True, 643 }, 644 { 645 "name": "__cpp_lib_gcd_lcm", 646 "values": {"c++17": 201606}, 647 "headers": ["numeric"], 648 }, 649 { 650 "name": "__cpp_lib_generate_random", 651 "values": {"c++26": 202403}, # P1068R11: Vector API for random number generation 652 "headers": ["random"], 653 "unimplemented": True, 654 }, 655 { 656 "name": "__cpp_lib_generic_associative_lookup", 657 "values": {"c++14": 201304}, 658 "headers": ["map", "set"], 659 }, 660 { 661 "name": "__cpp_lib_generic_unordered_lookup", 662 "values": {"c++20": 201811}, 663 "headers": ["unordered_map", "unordered_set"], 664 }, 665 { 666 "name": "__cpp_lib_hardware_interference_size", 667 "values": {"c++17": 201703}, 668 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE))", 669 "libcxx_guard": "defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE)", 670 "headers": ["new"], 671 }, 672 { 673 "name": "__cpp_lib_has_unique_object_representations", 674 "values": {"c++17": 201606}, 675 "headers": ["type_traits"], 676 }, 677 { 678 "name": "__cpp_lib_hazard_pointer", 679 "values": {"c++26": 202306}, # P2530R3 Hazard Pointers for C++26 680 "headers": [ 681 "hazard_pointer" # TODO verify this entry since the paper was underspecified. 682 ], 683 "unimplemented": True, 684 }, 685 { 686 "name": "__cpp_lib_hypot", 687 "values": {"c++17": 201603}, 688 "headers": ["cmath"], 689 }, 690 { 691 "name": "__cpp_lib_incomplete_container_elements", 692 "values": {"c++17": 201505}, 693 "headers": ["forward_list", "list", "vector"], 694 }, 695 { 696 "name": "__cpp_lib_inplace_vector", 697 "values": {"c++26": 202406}, # P0843R14 inplace_vector 698 "headers": ["inplace_vector"], 699 "unimplemented": True, 700 }, 701 { 702 "name": "__cpp_lib_int_pow2", 703 "values": {"c++20": 202002}, 704 "headers": ["bit"], 705 }, 706 { 707 "name": "__cpp_lib_integer_comparison_functions", 708 "values": {"c++20": 202002}, 709 "headers": ["utility"], 710 }, 711 { 712 "name": "__cpp_lib_integer_sequence", 713 "values": {"c++14": 201304}, 714 "headers": ["utility"], 715 }, 716 { 717 "name": "__cpp_lib_integral_constant_callable", 718 "values": {"c++14": 201304}, 719 "headers": ["type_traits"], 720 }, 721 { 722 "name": "__cpp_lib_interpolate", 723 "values": {"c++20": 201902}, 724 "headers": ["cmath", "numeric"], 725 }, 726 { 727 "name": "__cpp_lib_invoke", 728 "values": {"c++17": 201411}, 729 "headers": ["functional"], 730 }, 731 { 732 "name": "__cpp_lib_invoke_r", 733 "values": {"c++23": 202106}, 734 "headers": ["functional"], 735 }, 736 { 737 "name": "__cpp_lib_ios_noreplace", 738 "values": {"c++23": 202207}, 739 "headers": ["ios"], 740 }, 741 { 742 "name": "__cpp_lib_is_aggregate", 743 "values": {"c++17": 201703}, 744 "headers": ["type_traits"], 745 }, 746 { 747 "name": "__cpp_lib_is_constant_evaluated", 748 "values": {"c++20": 201811}, 749 "headers": ["type_traits"], 750 }, 751 { 752 "name": "__cpp_lib_is_final", 753 "values": {"c++14": 201402}, 754 "headers": ["type_traits"], 755 }, 756 { 757 "name": "__cpp_lib_is_implicit_lifetime", 758 "values": {"c++23": 202302}, 759 "headers": ["type_traits"], 760 "test_suite_guard": "__has_builtin(__builtin_is_implicit_lifetime)", 761 "libcxx_guard": "__has_builtin(__builtin_is_implicit_lifetime)", 762 }, 763 { 764 "name": "__cpp_lib_is_invocable", 765 "values": {"c++17": 201703}, 766 "headers": ["type_traits"], 767 }, 768 { 769 "name": "__cpp_lib_is_layout_compatible", 770 "values": {"c++20": 201907}, 771 "headers": ["type_traits"], 772 "unimplemented": True, 773 }, 774 { 775 "name": "__cpp_lib_is_nothrow_convertible", 776 "values": {"c++20": 201806}, 777 "headers": ["type_traits"], 778 }, 779 { 780 "name": "__cpp_lib_is_null_pointer", 781 "values": {"c++14": 201309}, 782 "headers": ["type_traits"], 783 }, 784 { 785 "name": "__cpp_lib_is_pointer_interconvertible", 786 "values": {"c++20": 201907}, 787 "headers": ["type_traits"], 788 "unimplemented": True, 789 }, 790 { 791 "name": "__cpp_lib_is_scoped_enum", 792 "values": {"c++23": 202011}, 793 "headers": ["type_traits"], 794 }, 795 { 796 "name": "__cpp_lib_is_swappable", 797 "values": {"c++17": 201603}, 798 "headers": ["type_traits"], 799 }, 800 { 801 "name": "__cpp_lib_is_virtual_base_of", 802 "values": { 803 "c++26": 202406 # P2985R0 A type trait for detecting virtual base classes 804 }, 805 "headers": ["type_traits"], 806 "test_suite_guard": "__has_builtin(__builtin_is_virtual_base_of)", 807 "libcxx_guard": "__has_builtin(__builtin_is_virtual_base_of)", 808 }, 809 { 810 "name": "__cpp_lib_is_within_lifetime", 811 # Note this name was changed from "__cpp_lib_within_lifetime" when the paper was adopted 812 # https://github.com/cplusplus/draft/commit/0facada4cadd97e1ba15bfaea76a804f1dc5c309 813 "values": { 814 "c++26": 202306 # P2641R4 Checking if a union alternative is active 815 }, 816 "headers": ["type_traits"], 817 "unimplemented": True, 818 }, 819 { 820 "name": "__cpp_lib_jthread", 821 "values": {"c++20": 201911}, 822 "headers": ["stop_token", "thread"], 823 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)", 824 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC", 825 }, 826 { 827 "name": "__cpp_lib_latch", 828 "values": {"c++20": 201907}, 829 "headers": ["latch"], 830 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)", 831 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC", 832 }, 833 { 834 "name": "__cpp_lib_launder", 835 "values": {"c++17": 201606}, 836 "headers": ["new"], 837 }, 838 { 839 "name": "__cpp_lib_linalg", 840 "values": { 841 "c++26": 202311 # P1673 A free function linear algebra interface based on the BLAS 842 }, 843 "headers": ["linalg"], 844 "unimplemented": True, 845 }, 846 { 847 "name": "__cpp_lib_list_remove_return_type", 848 "values": {"c++20": 201806}, 849 "headers": ["forward_list", "list"], 850 }, 851 { 852 "name": "__cpp_lib_logical_traits", 853 "values": {"c++17": 201510}, 854 "headers": ["type_traits"], 855 }, 856 { 857 "name": "__cpp_lib_make_from_tuple", 858 "values": {"c++17": 201606}, 859 "headers": ["tuple"], 860 }, 861 { 862 "name": "__cpp_lib_make_reverse_iterator", 863 "values": {"c++14": 201402}, 864 "headers": ["iterator"], 865 }, 866 { 867 "name": "__cpp_lib_make_unique", 868 "values": {"c++14": 201304}, 869 "headers": ["memory"], 870 }, 871 { 872 "name": "__cpp_lib_map_try_emplace", 873 "values": {"c++17": 201411}, 874 "headers": ["map"], 875 }, 876 { 877 "name": "__cpp_lib_math_constants", 878 "values": {"c++20": 201907}, 879 "headers": ["numbers"], 880 }, 881 { 882 "name": "__cpp_lib_math_special_functions", 883 "values": {"c++17": 201603}, 884 "headers": ["cmath"], 885 "unimplemented": True, 886 }, 887 { 888 "name": "__cpp_lib_mdspan", 889 "values": { 890 "c++23": 202207, 891 "c++26": 202406, # P2389R2 dextents Index Type Parameter 892 }, 893 "headers": ["mdspan"], 894 }, 895 { 896 "name": "__cpp_lib_memory_resource", 897 "values": {"c++17": 201603}, 898 "headers": ["memory_resource"], 899 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR", 900 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR", 901 }, 902 { 903 "name": "__cpp_lib_modules", 904 "values": {"c++23": 202207}, 905 "headers": [], 906 }, 907 { 908 "name": "__cpp_lib_move_iterator_concept", 909 "values": {"c++20": 202207}, 910 "headers": ["iterator"], 911 }, 912 { 913 "name": "__cpp_lib_move_only_function", 914 "values": {"c++23": 202110}, 915 "headers": ["functional"], 916 "unimplemented": True, 917 }, 918 { 919 "name": "__cpp_lib_node_extract", 920 "values": {"c++17": 201606}, 921 "headers": ["map", "set", "unordered_map", "unordered_set"], 922 }, 923 { 924 "name": "__cpp_lib_nonmember_container_access", 925 "values": {"c++17": 201411}, 926 "headers": [ 927 "array", 928 "deque", 929 "forward_list", 930 "iterator", 931 "list", 932 "map", 933 "regex", 934 "set", 935 "string", 936 "unordered_map", 937 "unordered_set", 938 "vector", 939 ], 940 }, 941 { 942 "name": "__cpp_lib_not_fn", 943 "values": { 944 "c++17": 201603, 945 "c++26": 202306, # P2714R1 Bind front and back to NTTP callables 946 }, 947 "headers": ["functional"], 948 }, 949 { 950 "name": "__cpp_lib_null_iterators", 951 "values": {"c++14": 201304}, 952 "headers": ["iterator"], 953 }, 954 { 955 "name": "__cpp_lib_optional", 956 "values": { 957 "c++17": 201606, 958 "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant 959 "c++23": 202110, # P0798R8 Monadic operations for std::optional + LWG3621 Remove feature-test macro __cpp_lib_monadic_optional 960 }, 961 "headers": ["optional"], 962 }, 963 { 964 "name": "__cpp_lib_optional_range_support", 965 "values": {"c++26": 202406}, # P3168R2 Give std::optional Range Support 966 "headers": ["optional"], 967 "unimplemented": True, 968 }, 969 { 970 "name": "__cpp_lib_out_ptr", 971 "values": { 972 "c++23": 202106, 973 "c++26": 202311, # P2833R2 Freestanding Library: inout expected span 974 }, 975 "headers": ["memory"], 976 }, 977 { 978 "name": "__cpp_lib_parallel_algorithm", 979 "values": {"c++17": 201603}, 980 "headers": ["algorithm", "numeric"], 981 "unimplemented": True, 982 }, 983 { 984 "name": "__cpp_lib_philox_engine", 985 "values": { 986 "c++26": 202406 987 }, # P2075R6 Philox as an extension of the C++ RNG engines 988 # Note the paper mentions 202310L as value, which differs from the typical procedure. 989 "headers": ["random"], 990 "unimplemented": True, 991 }, 992 { 993 "name": "__cpp_lib_polymorphic_allocator", 994 "values": {"c++20": 201902}, 995 "headers": ["memory_resource"], 996 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR", 997 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR", 998 }, 999 { 1000 "name": "__cpp_lib_print", 1001 "values": { 1002 "c++23": 202207, 1003 # "c++26": 202403, # P3107R5: Permit an efficient implementation of std::print 1004 # "c++26": 202406, # P3235R3 std::print more types faster with less memory 1005 }, 1006 "headers": ["ostream", "print"], 1007 }, 1008 { 1009 "name": "__cpp_lib_quoted_string_io", 1010 "values": {"c++14": 201304}, 1011 "headers": ["iomanip"], 1012 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_LOCALIZATION", 1013 "libcxx_guard": "_LIBCPP_HAS_LOCALIZATION", 1014 }, 1015 { 1016 "name": "__cpp_lib_ranges", 1017 "values": { 1018 "c++20": 202110, # P2415R2 What is a view? 1019 "c++23": 202406, # P2997R1 Removing the common reference requirement from the indirectly invocable concepts (implemented as a DR against C++20) 1020 }, 1021 "headers": ["algorithm", "functional", "iterator", "memory", "ranges"], 1022 }, 1023 { 1024 "name": "__cpp_lib_ranges_as_const", 1025 "values": { 1026 "c++23": 202207 # P2278R4 cbegin should always return a constant iterator 1027 # 202311 # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility 1028 }, 1029 "headers": ["ranges"], 1030 "unimplemented": True, 1031 }, 1032 { 1033 "name": "__cpp_lib_ranges_as_rvalue", 1034 "values": {"c++23": 202207}, 1035 "headers": ["ranges"], 1036 }, 1037 { 1038 "name": "__cpp_lib_ranges_chunk", 1039 "values": {"c++23": 202202}, 1040 "headers": ["ranges"], 1041 "unimplemented": True, 1042 }, 1043 { 1044 "name": "__cpp_lib_ranges_chunk_by", 1045 "values": {"c++23": 202202}, 1046 "headers": ["ranges"], 1047 }, 1048 { 1049 "name": "__cpp_lib_ranges_concat", 1050 "values": {"c++26": 202403}, # P2542R8: views::concat 1051 "headers": ["ranges"], 1052 "unimplemented": True, 1053 }, 1054 { 1055 "name": "__cpp_lib_ranges_contains", 1056 "values": {"c++23": 202207}, 1057 "headers": ["algorithm"], 1058 }, 1059 { 1060 "name": "__cpp_lib_ranges_find_last", 1061 "values": {"c++23": 202207}, 1062 "headers": ["algorithm"], 1063 }, 1064 { 1065 "name": "__cpp_lib_ranges_iota", 1066 "values": {"c++23": 202202}, 1067 "headers": ["numeric"], 1068 "unimplemented": True, 1069 }, 1070 { 1071 "name": "__cpp_lib_ranges_join_with", 1072 "values": {"c++23": 202202}, 1073 "headers": ["ranges"], 1074 "unimplemented": True, 1075 }, 1076 { 1077 "name": "__cpp_lib_ranges_repeat", 1078 "values": {"c++23": 202207}, 1079 "headers": ["ranges"], 1080 }, 1081 { 1082 "name": "__cpp_lib_ranges_slide", 1083 "values": {"c++23": 202202}, 1084 "headers": ["ranges"], 1085 "unimplemented": True, 1086 }, 1087 { 1088 "name": "__cpp_lib_ranges_starts_ends_with", 1089 "values": {"c++23": 202106}, 1090 "headers": ["algorithm"], 1091 }, 1092 { 1093 "name": "__cpp_lib_ranges_to_container", 1094 "values": {"c++23": 202202}, 1095 "headers": ["ranges"], 1096 }, 1097 { 1098 "name": "__cpp_lib_ranges_zip", 1099 "values": {"c++23": 202110}, 1100 "headers": ["ranges", "tuple", "utility"], 1101 "unimplemented": True, 1102 }, 1103 { 1104 "name": "__cpp_lib_ratio", 1105 "values": {"c++26": 202306}, # P2734R0 Adding the new SI prefixes 1106 "headers": ["ratio"], 1107 }, 1108 { 1109 "name": "__cpp_lib_raw_memory_algorithms", 1110 "values": {"c++17": 201606}, 1111 "headers": ["memory"], 1112 }, 1113 { 1114 "name": "__cpp_lib_rcu", 1115 "values": {"c++26": 202306}, # P2545R4 Read-Copy Update (RCU) 1116 "headers": [ 1117 "rcu" # TODO verify this entry since the paper was underspecified. 1118 ], 1119 "unimplemented": True, 1120 }, 1121 { 1122 "name": "__cpp_lib_reference_from_temporary", 1123 "values": {"c++23": 202202}, 1124 "headers": ["type_traits"], 1125 "unimplemented": True, 1126 }, 1127 { 1128 "name": "__cpp_lib_reference_wrapper", 1129 "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper 1130 "headers": ["functional"], 1131 }, 1132 { 1133 "name": "__cpp_lib_remove_cvref", 1134 "values": {"c++20": 201711}, 1135 "headers": ["type_traits"], 1136 }, 1137 { 1138 "name": "__cpp_lib_result_of_sfinae", 1139 "values": {"c++14": 201210}, 1140 "headers": ["functional", "type_traits"], 1141 }, 1142 { 1143 "name": "__cpp_lib_robust_nonmodifying_seq_ops", 1144 "values": {"c++14": 201304}, 1145 "headers": ["algorithm"], 1146 }, 1147 { 1148 "name": "__cpp_lib_sample", 1149 "values": {"c++17": 201603}, 1150 "headers": ["algorithm"], 1151 }, 1152 { 1153 "name": "__cpp_lib_saturation_arithmetic", 1154 "values": {"c++26": 202311}, # P0543R3 Saturation arithmetic 1155 "headers": ["numeric"], 1156 }, 1157 { 1158 "name": "__cpp_lib_scoped_lock", 1159 "values": {"c++17": 201703}, 1160 "headers": ["mutex"], 1161 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS", 1162 "libcxx_guard": "_LIBCPP_HAS_THREADS", 1163 }, 1164 { 1165 "name": "__cpp_lib_semaphore", 1166 "values": {"c++20": 201907}, 1167 "headers": ["semaphore"], 1168 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)", 1169 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC", 1170 }, 1171 { 1172 "name": "__cpp_lib_senders", 1173 "values": {"c++26": 202406}, # P2300R10 std::execution 1174 "headers": ["execution"], 1175 "unimplemented": True, 1176 }, 1177 { 1178 "name": "__cpp_lib_shared_mutex", 1179 "values": {"c++17": 201505}, 1180 "headers": ["shared_mutex"], 1181 "test_suite_guard": "_LIBCPP_HAS_THREADS", 1182 "libcxx_guard": "_LIBCPP_HAS_THREADS", 1183 }, 1184 { 1185 "name": "__cpp_lib_shared_ptr_arrays", 1186 "values": {"c++17": 201611, "c++20": 201707}, 1187 "headers": ["memory"], 1188 }, 1189 { 1190 "name": "__cpp_lib_shared_ptr_weak_type", 1191 "values": {"c++17": 201606}, 1192 "headers": ["memory"], 1193 }, 1194 { 1195 "name": "__cpp_lib_shared_timed_mutex", 1196 "values": {"c++14": 201402}, 1197 "headers": ["shared_mutex"], 1198 "test_suite_guard": "_LIBCPP_HAS_THREADS", 1199 "libcxx_guard": "_LIBCPP_HAS_THREADS", 1200 }, 1201 { 1202 "name": "__cpp_lib_shift", 1203 "values": {"c++20": 201806}, 1204 "headers": ["algorithm"], 1205 }, 1206 { 1207 "name": "__cpp_lib_smart_ptr_for_overwrite", 1208 "values": {"c++20": 202002}, 1209 "headers": ["memory"], 1210 }, 1211 { 1212 "name": "__cpp_lib_smart_ptr_owner_equality", 1213 "values": { 1214 "c++26": 202306 # P1901R2 Enabling the Use of weak_ptr as Keys in Unordered Associative Containers 1215 }, 1216 "headers": ["memory"], 1217 "unimplemented": True, 1218 }, 1219 { 1220 "name": "__cpp_lib_source_location", 1221 "values": {"c++20": 201907}, 1222 "headers": ["source_location"], 1223 }, 1224 { 1225 "name": "__cpp_lib_span", 1226 "values": { 1227 "c++20": 202002, 1228 # "c++26": 202311, # P2821R5 span.at() 1229 # 202311 # P2833R2 Freestanding Library: inout expected span 1230 }, 1231 "headers": ["span"], 1232 }, 1233 { 1234 "name": "__cpp_lib_span_at", 1235 "values": {"c++26": 202311}, # P2821R3 span.at() 1236 "headers": ["span"], 1237 }, 1238 { 1239 "name": "__cpp_lib_span_initializer_list", 1240 "values": {"c++26": 202311}, # P2447R6 std::span over an initializer list 1241 "headers": ["span"], 1242 }, 1243 { 1244 "name": "__cpp_lib_spanstream", 1245 "values": {"c++23": 202106}, 1246 "headers": ["spanstream"], 1247 "unimplemented": True, 1248 }, 1249 { 1250 "name": "__cpp_lib_ssize", 1251 "values": {"c++20": 201902}, 1252 "headers": ["iterator"], 1253 }, 1254 { 1255 "name": "__cpp_lib_sstream_from_string_view", 1256 "values": { 1257 "c++26": 202306 # P2495R3 Interfacing stringstreams with string_view 1258 }, 1259 "headers": ["sstream"], 1260 }, 1261 { 1262 "name": "__cpp_lib_stacktrace", 1263 "values": {"c++23": 202011}, 1264 "headers": ["stacktrace"], 1265 "unimplemented": True, 1266 }, 1267 { 1268 "name": "__cpp_lib_starts_ends_with", 1269 "values": {"c++20": 201711}, 1270 "headers": ["string", "string_view"], 1271 }, 1272 { 1273 "name": "__cpp_lib_stdatomic_h", 1274 "values": {"c++23": 202011}, 1275 "headers": ["stdatomic.h"], 1276 }, 1277 { 1278 "name": "__cpp_lib_string_contains", 1279 "values": {"c++23": 202011}, 1280 "headers": ["string", "string_view"], 1281 }, 1282 { 1283 "name": "__cpp_lib_string_resize_and_overwrite", 1284 "values": {"c++23": 202110}, 1285 "headers": ["string"], 1286 }, 1287 { 1288 "name": "__cpp_lib_string_udls", 1289 "values": {"c++14": 201304}, 1290 "headers": ["string"], 1291 }, 1292 { 1293 "name": "__cpp_lib_string_view", 1294 "values": { 1295 "c++17": 201606, 1296 "c++20": 201803, 1297 "c++26": 202403, # P2591R5: Concatenation of strings and string views 1298 }, 1299 "headers": ["string", "string_view"], 1300 }, 1301 { 1302 "name": "__cpp_lib_submdspan", 1303 "values": { 1304 "c++26": 202306, # P2630R4: submdspan 1305 # "c++26": 202403, # P2642R6: Padded mdspan layouts 1306 }, 1307 "headers": ["mdspan"], 1308 "unimplemented": True, 1309 }, 1310 { 1311 "name": "__cpp_lib_syncbuf", 1312 "values": {"c++20": 201803}, 1313 "headers": ["syncstream"], 1314 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM", 1315 "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM", 1316 }, 1317 { 1318 "name": "__cpp_lib_text_encoding", 1319 "values": { 1320 "c++26": 202306 # P1885R12 Naming Text Encodings to Demystify Them 1321 }, 1322 "headers": ["text_encoding"], 1323 "unimplemented": True, 1324 }, 1325 { 1326 "name": "__cpp_lib_three_way_comparison", 1327 "values": {"c++20": 201907}, 1328 "headers": ["compare"], 1329 }, 1330 { 1331 "name": "__cpp_lib_to_address", 1332 "values": {"c++20": 201711}, 1333 "headers": ["memory"], 1334 }, 1335 { 1336 "name": "__cpp_lib_to_array", 1337 "values": {"c++20": 201907}, 1338 "headers": ["array"], 1339 }, 1340 { 1341 "name": "__cpp_lib_to_chars", 1342 "values": { 1343 "c++17": 201611, 1344 "c++26": 202306, # P2497R0 Testing for success or failure of <charconv> functions 1345 }, 1346 "headers": ["charconv"], 1347 "unimplemented": True, 1348 }, 1349 { 1350 "name": "__cpp_lib_to_string", 1351 "values": {"c++26": 202306}, # P2587R3 to_string or not to_string 1352 "headers": ["string"], 1353 "unimplemented": True, 1354 }, 1355 { 1356 "name": "__cpp_lib_to_underlying", 1357 "values": {"c++23": 202102}, 1358 "headers": ["utility"], 1359 }, 1360 { 1361 "name": "__cpp_lib_transformation_trait_aliases", 1362 "values": {"c++14": 201304}, 1363 "headers": ["type_traits"], 1364 }, 1365 { 1366 "name": "__cpp_lib_transparent_operators", 1367 "values": {"c++14": 201210, "c++17": 201510}, 1368 "headers": ["functional", "memory"], 1369 }, 1370 { 1371 "name": "__cpp_lib_tuple_element_t", 1372 "values": {"c++14": 201402}, 1373 "headers": ["tuple"], 1374 }, 1375 { 1376 "name": "__cpp_lib_tuple_like", 1377 "values": { 1378 "c++23": 202207, # P2165R4 Compatibility between tuple, pair and tuple-like objects 1379 "c++26": 202311, # P2819R2 Add tuple protocol to complex (implemented) 1380 }, 1381 "headers": ["map", "tuple", "unordered_map", "utility"], 1382 "unimplemented": True, 1383 }, 1384 { 1385 "name": "__cpp_lib_tuples_by_type", 1386 "values": {"c++14": 201304}, 1387 "headers": ["tuple", "utility"], 1388 }, 1389 { 1390 "name": "__cpp_lib_type_identity", 1391 "values": {"c++20": 201806}, 1392 "headers": ["type_traits"], 1393 }, 1394 { 1395 "name": "__cpp_lib_type_trait_variable_templates", 1396 "values": {"c++17": 201510}, 1397 "headers": ["type_traits"], 1398 }, 1399 { 1400 "name": "__cpp_lib_uncaught_exceptions", 1401 "values": {"c++17": 201411}, 1402 "headers": ["exception"], 1403 }, 1404 { 1405 "name": "__cpp_lib_unordered_map_try_emplace", 1406 "values": {"c++17": 201411}, 1407 "headers": ["unordered_map"], 1408 }, 1409 { 1410 "name": "__cpp_lib_unreachable", 1411 "values": {"c++23": 202202}, 1412 "headers": ["utility"], 1413 }, 1414 { 1415 "name": "__cpp_lib_unwrap_ref", 1416 "values": {"c++20": 201811}, 1417 "headers": ["functional"], 1418 }, 1419 { 1420 "name": "__cpp_lib_variant", 1421 "values": { 1422 "c++17": 202102, # std::visit for classes derived from std::variant 1423 "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant 1424 "c++26": 202306, # P2637R3 Member visit 1425 }, 1426 "headers": ["variant"], 1427 }, 1428 { 1429 "name": "__cpp_lib_void_t", 1430 "values": {"c++17": 201411}, 1431 "headers": ["type_traits"], 1432 }, 1433 ] 1434] 1435 1436assert feature_test_macros == sorted(feature_test_macros, key=lambda tc: tc["name"]) 1437for tc in feature_test_macros: 1438 assert tc["headers"] == sorted(tc["headers"]), tc 1439 assert ("libcxx_guard" in tc) == ("test_suite_guard" in tc), tc 1440 valid_keys = ["name", "values", "headers", "libcxx_guard", "test_suite_guard", "unimplemented"] 1441 assert all(key in valid_keys for key in tc.keys()), tc 1442 1443# Map from each header to the Lit annotations that should be used for 1444# tests that include that header. 1445# 1446# For example, when threads are not supported, any test that includes 1447# <thread> should be marked as UNSUPPORTED, because including <thread> 1448# is a hard error in that case. 1449lit_markup = { 1450 "barrier": ["UNSUPPORTED: no-threads"], 1451 "filesystem": ["UNSUPPORTED: no-filesystem"], 1452 "fstream": ["UNSUPPORTED: no-localization"], 1453 "iomanip": ["UNSUPPORTED: no-localization"], 1454 "ios": ["UNSUPPORTED: no-localization"], 1455 "iostream": ["UNSUPPORTED: no-localization"], 1456 "istream": ["UNSUPPORTED: no-localization"], 1457 "latch": ["UNSUPPORTED: no-threads"], 1458 "locale": ["UNSUPPORTED: no-localization"], 1459 "mutex": ["UNSUPPORTED: no-threads"], 1460 "ostream": ["UNSUPPORTED: no-localization"], 1461 "print": ["UNSUPPORTED: no-filesystem"], 1462 "regex": ["UNSUPPORTED: no-localization"], 1463 "semaphore": ["UNSUPPORTED: no-threads"], 1464 "shared_mutex": ["UNSUPPORTED: no-threads"], 1465 "sstream": ["UNSUPPORTED: no-localization"], 1466 "syncstream": ["UNSUPPORTED: no-localization"], 1467 "stdatomic.h": ["UNSUPPORTED: no-threads"], 1468 "stop_token": ["UNSUPPORTED: no-threads"], 1469 "thread": ["UNSUPPORTED: no-threads"], 1470} 1471 1472 1473def get_std_dialects(): 1474 std_dialects = ["c++14", "c++17", "c++20", "c++23", "c++26"] 1475 return list(std_dialects) 1476 1477 1478def get_first_std(d): 1479 for s in get_std_dialects(): 1480 if s in d.keys(): 1481 return s 1482 return None 1483 1484 1485def get_last_std(d): 1486 rev_dialects = get_std_dialects() 1487 rev_dialects.reverse() 1488 for s in rev_dialects: 1489 if s in d.keys(): 1490 return s 1491 return None 1492 1493 1494def get_std_before(d, std): 1495 std_dialects = get_std_dialects() 1496 candidates = std_dialects[0 : std_dialects.index(std)] 1497 candidates.reverse() 1498 for cand in candidates: 1499 if cand in d.keys(): 1500 return cand 1501 return None 1502 1503 1504def get_value_before(d, std): 1505 new_std = get_std_before(d, std) 1506 if new_std is None: 1507 return None 1508 return d[new_std] 1509 1510 1511def get_for_std(d, std): 1512 # This catches the C++11 case for which there should be no defined feature 1513 # test macros. 1514 std_dialects = get_std_dialects() 1515 if std not in std_dialects: 1516 return None 1517 # Find the value for the newest C++ dialect between C++14 and std 1518 std_list = list(std_dialects[0 : std_dialects.index(std) + 1]) 1519 std_list.reverse() 1520 for s in std_list: 1521 if s in d.keys(): 1522 return d[s] 1523 return None 1524 1525 1526def get_std_number(std): 1527 return std.replace("c++", "") 1528 1529 1530""" 1531 Functions to produce the <version> header 1532""" 1533 1534 1535def produce_macros_definition_for_std(std): 1536 result = "" 1537 indent = 55 1538 for tc in feature_test_macros: 1539 if std not in tc["values"]: 1540 continue 1541 inner_indent = 1 1542 if "test_suite_guard" in tc.keys(): 1543 result += "# if %s\n" % tc["libcxx_guard"] 1544 inner_indent += 2 1545 if get_value_before(tc["values"], std) is not None: 1546 assert "test_suite_guard" not in tc.keys() 1547 result += "# undef %s\n" % tc["name"] 1548 line = "#%sdefine %s" % ((" " * inner_indent), tc["name"]) 1549 line += " " * (indent - len(line)) 1550 line += " %sL" % tc["values"][std] 1551 if "unimplemented" in tc.keys(): 1552 line = "// " + line 1553 result += line 1554 result += "\n" 1555 if "test_suite_guard" in tc.keys(): 1556 result += "# endif\n" 1557 return result.strip() 1558 1559 1560def produce_macros_definitions(): 1561 macro_definition_template = """#if _LIBCPP_STD_VER >= {std_number} 1562{macro_definition} 1563#endif""" 1564 1565 macros_definitions = [] 1566 for std in get_std_dialects(): 1567 macros_definitions.append( 1568 macro_definition_template.format( 1569 std_number=get_std_number(std), 1570 macro_definition=produce_macros_definition_for_std(std), 1571 ) 1572 ) 1573 1574 return "\n\n".join(macros_definitions) 1575 1576 1577def chunks(l, n): 1578 """Yield successive n-sized chunks from l.""" 1579 for i in range(0, len(l), n): 1580 yield l[i : i + n] 1581 1582 1583def produce_version_synopsis(): 1584 indent = 56 1585 header_indent = 56 + len("20XXYYL ") 1586 result = "" 1587 1588 def indent_to(s, val): 1589 if len(s) >= val: 1590 return s 1591 s += " " * (val - len(s)) 1592 return s 1593 1594 line = indent_to("Macro name", indent) + "Value" 1595 line = indent_to(line, header_indent) + "Headers" 1596 result += line + "\n" 1597 for tc in feature_test_macros: 1598 prev_defined_std = get_last_std(tc["values"]) 1599 line = "{name: <{indent}}{value}L ".format( 1600 name=tc["name"], indent=indent, value=tc["values"][prev_defined_std] 1601 ) 1602 headers = list(tc["headers"]) 1603 headers.remove("version") 1604 for chunk in chunks(headers, 3): 1605 line = indent_to(line, header_indent) 1606 chunk = ["<%s>" % header for header in chunk] 1607 line += " ".join(chunk) 1608 result += line 1609 result += "\n" 1610 line = "" 1611 while True: 1612 prev_defined_std = get_std_before(tc["values"], prev_defined_std) 1613 if prev_defined_std is None: 1614 break 1615 result += "%s%sL // %s\n" % ( 1616 indent_to("", indent), 1617 tc["values"][prev_defined_std], 1618 prev_defined_std.replace("c++", "C++"), 1619 ) 1620 return result 1621 1622 1623def produce_version_header(): 1624 template = """// -*- C++ -*- 1625//===----------------------------------------------------------------------===// 1626// 1627// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 1628// See https://llvm.org/LICENSE.txt for license information. 1629// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 1630// 1631//===----------------------------------------------------------------------===// 1632 1633#ifndef _LIBCPP_VERSIONH 1634#define _LIBCPP_VERSIONH 1635 1636/* 1637 version synopsis 1638 1639{synopsis} 1640 1641*/ 1642 1643#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) 1644# include <__cxx03/version> 1645#else 1646# include <__config> 1647 1648# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 1649# pragma GCC system_header 1650# endif 1651 1652// clang-format off 1653 1654{cxx_macros} 1655 1656#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) 1657 1658// clang-format on 1659 1660#endif // _LIBCPP_VERSIONH 1661""" 1662 1663 version_str = template.format( 1664 synopsis=produce_version_synopsis().strip(), 1665 cxx_macros=produce_macros_definitions(), 1666 ) 1667 version_header_path = os.path.join(include_path, "version") 1668 with open(version_header_path, "w", newline="\n") as f: 1669 f.write(version_str) 1670 1671 1672""" 1673 Functions to produce test files 1674""" 1675 1676test_types = { 1677 "undefined": """ 1678# ifdef {name} 1679# error "{name} should not be defined before {std_first}" 1680# endif 1681""", 1682 "test_suite_guard": """ 1683# if {test_suite_guard} 1684# ifndef {name} 1685# error "{name} should be defined in {std}" 1686# endif 1687# if {name} != {value} 1688# error "{name} should have the value {value} in {std}" 1689# endif 1690# else 1691# ifdef {name} 1692# error "{name} should not be defined when the requirement '{test_suite_guard}' is not met!" 1693# endif 1694# endif 1695""", 1696 "unimplemented": """ 1697# if !defined(_LIBCPP_VERSION) 1698# ifndef {name} 1699# error "{name} should be defined in {std}" 1700# endif 1701# if {name} != {value} 1702# error "{name} should have the value {value} in {std}" 1703# endif 1704# else // _LIBCPP_VERSION 1705# ifdef {name} 1706# error "{name} should not be defined because it is unimplemented in libc++!" 1707# endif 1708# endif 1709""", 1710 "defined": """ 1711# ifndef {name} 1712# error "{name} should be defined in {std}" 1713# endif 1714# if {name} != {value} 1715# error "{name} should have the value {value} in {std}" 1716# endif 1717""", 1718} 1719 1720 1721def generate_std_test(test_list, std): 1722 result = "" 1723 for tc in test_list: 1724 val = get_for_std(tc["values"], std) 1725 if val is not None: 1726 val = "%sL" % val 1727 if val is None: 1728 result += test_types["undefined"].format( 1729 name=tc["name"], std_first=get_first_std(tc["values"]) 1730 ) 1731 elif "unimplemented" in tc.keys(): 1732 result += test_types["unimplemented"].format( 1733 name=tc["name"], value=val, std=std 1734 ) 1735 elif "test_suite_guard" in tc.keys(): 1736 result += test_types["test_suite_guard"].format( 1737 name=tc["name"], 1738 value=val, 1739 std=std, 1740 test_suite_guard=tc["test_suite_guard"], 1741 ) 1742 else: 1743 result += test_types["defined"].format(name=tc["name"], value=val, std=std) 1744 return result.strip() 1745 1746 1747def generate_std_tests(test_list): 1748 std_tests_template = """#if TEST_STD_VER < {first_std_number} 1749 1750{pre_std_test} 1751 1752{other_std_tests} 1753 1754#elif TEST_STD_VER > {penultimate_std_number} 1755 1756{last_std_test} 1757 1758#endif // TEST_STD_VER > {penultimate_std_number}""" 1759 1760 std_dialects = get_std_dialects() 1761 1762 other_std_tests = [] 1763 for std in std_dialects[:-1]: 1764 other_std_tests.append("#elif TEST_STD_VER == " + get_std_number(std)) 1765 other_std_tests.append(generate_std_test(test_list, std)) 1766 1767 std_tests = std_tests_template.format( 1768 first_std_number=get_std_number(std_dialects[0]), 1769 pre_std_test=generate_std_test(test_list, "c++11"), 1770 other_std_tests="\n\n".join(other_std_tests), 1771 penultimate_std_number=get_std_number(std_dialects[-2]), 1772 last_std_test=generate_std_test(test_list, std_dialects[-1]), 1773 ) 1774 1775 return std_tests 1776 1777 1778def generate_synopsis(test_list): 1779 max_name_len = max([len(tc["name"]) for tc in test_list]) 1780 indent = max_name_len + 8 1781 1782 def mk_line(prefix, suffix): 1783 return "{prefix: <{max_len}}{suffix}\n".format( 1784 prefix=prefix, suffix=suffix, max_len=indent 1785 ) 1786 1787 result = "" 1788 result += mk_line("/* Constant", "Value") 1789 for tc in test_list: 1790 prefix = " %s" % tc["name"] 1791 for std in [s for s in get_std_dialects() if s in tc["values"].keys()]: 1792 result += mk_line( 1793 prefix, "%sL [%s]" % (tc["values"][std], std.replace("c++", "C++")) 1794 ) 1795 prefix = "" 1796 result += "*/" 1797 return result 1798 1799 1800def produce_tests(): 1801 headers = set([h for tc in feature_test_macros for h in tc["headers"]]) 1802 for h in headers: 1803 test_list = [tc for tc in feature_test_macros if h in tc["headers"]] 1804 if not has_header(h): 1805 for tc in test_list: 1806 assert "unimplemented" in tc.keys() 1807 continue 1808 markup = "\n".join("// " + tag for tag in lit_markup.get(h, [])) 1809 test_body = """//===----------------------------------------------------------------------===// 1810// 1811// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 1812// See https://llvm.org/LICENSE.txt for license information. 1813// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 1814// 1815//===----------------------------------------------------------------------===// 1816// 1817// WARNING: This test was generated by {script_name} 1818// and should not be edited manually. 1819// 1820// clang-format off 1821{markup} 1822// <{header}> 1823 1824// Test the feature test macros defined by <{header}> 1825 1826{synopsis} 1827 1828#include <{header}> 1829#include "test_macros.h" 1830 1831{cxx_tests} 1832 1833""".format( 1834 script_name=script_name, 1835 header=h, 1836 markup=("\n{}\n".format(markup) if markup else ""), 1837 synopsis=generate_synopsis(test_list), 1838 cxx_tests=generate_std_tests(test_list), 1839 ) 1840 test_name = "{header}.version.compile.pass.cpp".format(header=h) 1841 out_path = os.path.join(macro_test_path, test_name) 1842 with open(out_path, "w", newline="\n") as f: 1843 f.write(test_body) 1844 1845 1846""" 1847 Produce documentation for the feature test macros 1848""" 1849 1850 1851def make_widths(grid): 1852 widths = [] 1853 for i in range(0, len(grid[0])): 1854 cell_width = 2 + max( 1855 reduce(lambda x, y: x + y, [[len(row[i])] for row in grid], []) 1856 ) 1857 widths += [cell_width] 1858 return widths 1859 1860 1861def create_table(grid, indent): 1862 indent_str = " " * indent 1863 col_widths = make_widths(grid) 1864 result = [indent_str + add_divider(col_widths, 2)] 1865 header_flag = 2 1866 for row_i in range(0, len(grid)): 1867 row = grid[row_i] 1868 line = indent_str + " ".join( 1869 [pad_cell(row[i], col_widths[i]) for i in range(0, len(row))] 1870 ) 1871 result.append(line.rstrip()) 1872 if row_i == len(grid) - 1: 1873 header_flag = 2 1874 if row[0].startswith("**"): 1875 header_flag += 1 1876 separator = indent_str + add_divider(col_widths, header_flag) 1877 result.append(separator.rstrip()) 1878 header_flag = 0 1879 return "\n".join(result) 1880 1881 1882def add_divider(widths, header_flag): 1883 if header_flag == 3: 1884 return "=".join(["=" * w for w in widths]) 1885 if header_flag == 2: 1886 return " ".join(["=" * w for w in widths]) 1887 if header_flag == 1: 1888 return "-".join(["-" * w for w in widths]) 1889 else: 1890 return " ".join(["-" * w for w in widths]) 1891 1892 1893def pad_cell(s, length, left_align=True): 1894 padding = (length - len(s)) * " " 1895 return s + padding 1896 1897 1898def get_status_table(): 1899 table = [["Macro Name", "Value"]] 1900 for std in get_std_dialects(): 1901 table += [["**" + std.replace("c++", "C++") + "**", ""]] 1902 for tc in feature_test_macros: 1903 if std not in tc["values"].keys(): 1904 continue 1905 value = "``%sL``" % tc["values"][std] 1906 if "unimplemented" in tc.keys(): 1907 value = "*unimplemented*" 1908 table += [["``%s``" % tc["name"], value]] 1909 return table 1910 1911 1912def produce_docs(): 1913 doc_str = """.. _FeatureTestMacroTable: 1914 1915========================== 1916Feature Test Macro Support 1917========================== 1918 1919.. contents:: 1920 :local: 1921 1922Overview 1923======== 1924 1925This file documents the feature test macros currently supported by libc++. 1926 1927.. _feature-status: 1928 1929Status 1930====== 1931 1932.. table:: Current Status 1933 :name: feature-status-table 1934 :widths: auto 1935 1936{status_tables} 1937 1938""".format( 1939 status_tables=create_table(get_status_table(), 4) 1940 ) 1941 1942 table_doc_path = os.path.join(docs_path, "FeatureTestMacroTable.rst") 1943 with open(table_doc_path, "w", newline="\n") as f: 1944 f.write(doc_str) 1945 1946 1947def get_ftms( 1948 data, std_dialects: List[str], use_implemented_status: bool 1949) -> Dict[str, Dict[str, Any]]: 1950 """Impementation for FeatureTestMacros.(standard|implemented)_ftms().""" 1951 result = dict() 1952 for feature in data: 1953 last = None 1954 entry = dict() 1955 implemented = True 1956 for std in std_dialects: 1957 if std not in feature["values"].keys(): 1958 if last == None: 1959 continue 1960 else: 1961 entry[std] = last 1962 else: 1963 if implemented: 1964 values = feature["values"][std] 1965 assert len(values) > 0, f"{feature['name']}[{std}] has no entries" 1966 for value in values: 1967 papers = list(values[value]) 1968 assert ( 1969 len(papers) > 0 1970 ), f"{feature['name']}[{std}][{value}] has no entries" 1971 for paper in papers: 1972 if use_implemented_status and not paper["implemented"]: 1973 implemented = False 1974 break 1975 if implemented: 1976 last = f"{value}L" 1977 else: 1978 break 1979 1980 entry[std] = last 1981 result[feature["name"]] = entry 1982 1983 return result 1984 1985 1986def generate_version_header_dialect_block(data: Dict[str, Any]) -> str: 1987 """Generates the contents of the version header for a dialect. 1988 1989 This generates the contents of a 1990 #if _LIBCPP_STD_VER >= XY 1991 #endif // _LIBCPP_STD_VER >= XY 1992 block. 1993 """ 1994 result = "" 1995 for element in data: 1996 for ftm, entry in element.items(): 1997 if not entry["implemented"]: 1998 # When a FTM is not implemented don't add the guards 1999 # or undefine the (possibly) defined macro. 2000 result += f'// define {ftm} {entry["value"]}\n' 2001 else: 2002 need_undef = entry["need_undef"] 2003 if entry["condition"]: 2004 result += f'# if {entry["condition"]}\n' 2005 if entry["need_undef"]: 2006 result += f"# undef {ftm}\n" 2007 result += f'# define {ftm} {entry["value"]}\n' 2008 result += f"# endif\n" 2009 else: 2010 if entry["need_undef"]: 2011 result += f"# undef {ftm}\n" 2012 result += f'# define {ftm} {entry["value"]}\n' 2013 2014 return result 2015 2016 2017def generate_version_header_implementation(data: Dict[str, Dict[str, Any]]) -> str: 2018 """Generates the body of the version header.""" 2019 2020 template = """#if _LIBCPP_STD_VER >= {dialect} 2021{feature_test_macros}#endif // _LIBCPP_STD_VER >= {dialect}""" 2022 2023 result = [] 2024 for std, ftms in data.items(): 2025 result.append( 2026 template.format( 2027 dialect=std, 2028 feature_test_macros=generate_version_header_dialect_block(ftms), 2029 ) 2030 ) 2031 2032 return "\n\n".join(result) 2033 2034 2035class FeatureTestMacros: 2036 """Provides all feature-test macro (FTM) output components. 2037 2038 The class has several generators to use the feature-test macros in libc++: 2039 - FTM status page 2040 - The version header and its tests 2041 2042 This class is not intended to duplicate 2043 https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#library-feature-test-macros 2044 SD-FeatureTest: Feature-Test Macros and Policies 2045 2046 Historically libc++ did not list all papers affecting a FTM, the new data 2047 structure is able to do that. However there is no intention to add the 2048 historical data. After papers have been implemented this information can be 2049 removed. For example, __cpp_lib_format's value 201907 requires 3 papers, 2050 once implemented it can be reduced to 1 paper and remove the paper number 2051 and title. This would reduce the size of the data. 2052 2053 The input data is stored in the following JSON format: 2054 [ # A list with multiple feature-test macro entries. 2055 { 2056 # required 2057 # The name of the feature test macro. These names should be unique and 2058 # sorted in the list. 2059 "name": "__cpp_lib_any", 2060 2061 # required 2062 # A map with the value of the FTM based on the language standard. Only 2063 # the versions in which the value of the FTM changes are listed. For 2064 # example, this macro's value does not change in C++20 so it does not 2065 # list C++20. If it changes in C++26, it will have entries for C++17 and 2066 # C++26. 2067 "values": { 2068 2069 # required 2070 # The language standard, also named dialect in this class. 2071 "c++17": { 2072 2073 # required 2074 # The value of the feature test macro. This contains an array with 2075 # one or more papers that need to be implemented before this value 2076 # is considered implemented. 2077 "201606": [ 2078 { 2079 # optional 2080 # Contains the paper number that is part of the FTM version. 2081 "number": "P0220R1", 2082 2083 # optional 2084 # Contains the title of the paper that is part of the FTM 2085 # version. 2086 "title": "Adopt Library Fundamentals V1 TS Components for C++17" 2087 2088 # required 2089 # The implementation status of the paper. 2090 "implemented": true 2091 } 2092 ] 2093 } 2094 }, 2095 2096 # required 2097 # A sorted list of headers that should provide the FTM. The header 2098 # <version> is automatically added to this list. This list could be 2099 # empty. For example, __cpp_lib_modules is only present in version. 2100 # Requiring the field makes it easier to detect accidental omission. 2101 "headers": [ 2102 "any" 2103 ], 2104 2105 # optional, required when libcxx_guard is present 2106 # This field is used only to generate the unit tests for the 2107 # feature-test macros. It can't depend on macros defined in <__config> 2108 # because the `test/std/` parts of the test suite are intended to be 2109 # portable to any C++ standard library implementation, not just libc++. 2110 # It may depend on 2111 # * macros defined by the compiler itself, or 2112 # * macros generated by CMake. 2113 # In some cases we add also depend on macros defined in 2114 # <__availability>. 2115 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR" 2116 2117 # optional, required when test_suite_guard is present 2118 # This field is used only to guard the feature-test macro in 2119 # <version>. It may be the same as `test_suite_guard`, or it may 2120 # depend on macros defined in <__config>. 2121 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR" 2122 }, 2123 ] 2124 """ 2125 2126 # The JSON data structure. 2127 __data = None 2128 2129 def __init__(self, filename: str): 2130 """Initializes the class with the JSON data in the file 'filename'.""" 2131 with open(filename) as f: 2132 self.__data = json.load(f) 2133 2134 @functools.cached_property 2135 def std_dialects(self) -> List[str]: 2136 """Returns the C++ dialects avaiable. 2137 2138 The available dialects are based on the 'c++xy' keys found the 'values' 2139 entries in '__data'. So when WG21 starts to feature-test macros for a 2140 future C++ Standard this dialect will automatically be available. 2141 2142 The return value is a sorted list with the C++ dialects used. Since FTM 2143 were added in C++14 the list will not contain C++03 or C++11. 2144 """ 2145 dialects = set() 2146 for feature in self.__data: 2147 keys = feature["values"].keys() 2148 assert len(keys) > 0, "'values' is empty" 2149 dialects |= keys 2150 2151 return sorted(list(dialects)) 2152 2153 @functools.cached_property 2154 def standard_ftms(self) -> Dict[str, Dict[str, Any]]: 2155 """Returns the FTM versions per dialect in the Standard. 2156 2157 This function does not use the 'implemented' flag. The output contains 2158 the versions used in the Standard. When a FTM in libc++ is not 2159 implemented according to the Standard to output may opt to show the 2160 expected value. 2161 2162 The result is a dict with the following content 2163 - key: Name of the feature test macro. 2164 - value: A dict with the following content: 2165 * key: The version of the C++ dialect. 2166 * value: The value of the feature-test macro. 2167 """ 2168 return get_ftms(self.__data, self.std_dialects, False) 2169 2170 @functools.cached_property 2171 def implemented_ftms(self) -> Dict[str, Dict[str, Any]]: 2172 """Returns the FTM versions per dialect implemented in libc++. 2173 2174 Unlike `get_std_dialect_versions` this function uses the 'implemented' 2175 flag. This returns the actual implementation status in libc++. 2176 2177 The result is a dict with the following content 2178 - key: Name of the feature test macro. 2179 - value: A dict with the following content: 2180 * key: The version of the C++ dialect. 2181 * value: The value of the feature-test macro. When a feature-test 2182 macro is not implemented its value is None. 2183 """ 2184 2185 return get_ftms(self.__data, self.std_dialects, True) 2186 2187 @functools.cached_property 2188 def ftm_metadata(self) -> Dict[str, Dict[str, Any]]: 2189 """Returns the metadata of the FTMs defined in the Standard. 2190 2191 The metadata does not depend on the C++ dialect used. 2192 The result is a dict with the following contents: 2193 - key: Name of the feature test macro. 2194 - value: A dict with the following content: 2195 * headers: The list of headers that should provide the FTM 2196 * test_suite_guard: The condition for testing the FTM in the test suite. 2197 * test_suite_guard: The condition for testing the FTM in the version header. 2198 """ 2199 result = dict() 2200 for feature in self.__data: 2201 entry = dict() 2202 entry["headers"] = feature["headers"] 2203 entry["test_suite_guard"] = feature.get("test_suite_guard", None) 2204 entry["libcxx_guard"] = feature.get("libcxx_guard", None) 2205 result[feature["name"]] = entry 2206 2207 return result 2208 2209 @property 2210 def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]: 2211 """Generates the body of the version header.""" 2212 result = dict() 2213 for std in self.std_dialects: 2214 result[get_std_number(std)] = list() 2215 2216 for ftm, values in self.standard_ftms.items(): 2217 last_value = None 2218 last_entry = None 2219 for std, value in values.items(): 2220 # When a newer Standard does not change the value of the macro 2221 # there is no need to redefine it with the same value. 2222 if last_value and value == last_value: 2223 continue 2224 last_value = value 2225 2226 entry = dict() 2227 entry["value"] = value 2228 entry["implemented"] = self.implemented_ftms[ftm][std] == self.standard_ftms[ftm][std] 2229 entry["need_undef"] = last_entry is not None and last_entry["implemented"] and entry["implemented"] 2230 entry["condition"] = self.ftm_metadata[ftm]["libcxx_guard"] 2231 2232 last_entry = entry 2233 result[get_std_number(std)].append(dict({ftm: entry})) 2234 2235 return result 2236 2237 @property 2238 def version_header(self) -> str: 2239 """Generates the version header.""" 2240 template = """// -*- C++ -*- 2241//===----------------------------------------------------------------------===// 2242// 2243// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 2244// See https://llvm.org/LICENSE.txt for license information. 2245// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 2246// 2247//===----------------------------------------------------------------------===// 2248 2249#ifndef _LIBCPP_VERSIONH 2250#define _LIBCPP_VERSIONH 2251 2252#include <__config> 2253 2254#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 2255# pragma GCC system_header 2256#endif 2257 2258{feature_test_macros} 2259 2260#endif // _LIBCPP_VERSIONH 2261""" 2262 return template.format( 2263 feature_test_macros=generate_version_header_implementation( 2264 self.version_header_implementation 2265 ) 2266 ) 2267 2268 2269def main(): 2270 produce_version_header() 2271 produce_tests() 2272 produce_docs() 2273 2274 # Example how to use the new version header generation function to generate 2275 # the file. 2276 if False: 2277 ftm = FeatureTestMacros( 2278 os.path.join( 2279 source_root, "test", "libcxx", "feature_test_macro", "test_data.json" 2280 ) 2281 ) 2282 version_header_path = os.path.join(include_path, "version") 2283 with open(version_header_path, "w", newline="\n") as f: 2284 f.write(ftm.version_header) 2285 2286 2287if __name__ == "__main__": 2288 main() 2289