From 1858f4f791bf7c38eccac988d66d55a6ee8b0f49 Mon Sep 17 00:00:00 2001 From: Vertexwahn Date: Mon, 3 Jun 2024 14:57:21 +0200 Subject: [PATCH] Project import generated by Copybara. GitOrigin-RevId: 4abdbff07fb9bde470617f08da039093fa5aca31 --- devertexwahn/.bazelrc | 18 +- devertexwahn/.bazelversion | 2 +- devertexwahn/MODULE.bazel | 6 +- third_party/abseil-cpp/CMake/AbseilDll.cmake | 86 +- .../abseil-cpp/CMake/AbseilHelpers.cmake | 2 +- third_party/abseil-cpp/CMakeLists.txt | 9 + third_party/abseil-cpp/absl/CMakeLists.txt | 4 +- .../abseil-cpp/absl/algorithm/container.h | 1 + third_party/abseil-cpp/absl/base/attributes.h | 26 + third_party/abseil-cpp/absl/base/config.h | 54 +- .../internal/compressed_tuple_test.cc | 28 + .../absl/container/internal/raw_hash_set.cc | 318 ++++++- .../absl/container/internal/raw_hash_set.h | 251 ++--- .../container/internal/raw_hash_set_test.cc | 16 + .../absl/copts/AbseilConfigureCopts.cmake | 2 +- .../absl/copts/GENERATED_AbseilCopts.cmake | 2 - .../abseil-cpp/absl/copts/GENERATED_copts.bzl | 2 - third_party/abseil-cpp/absl/copts/copts.py | 1 - .../absl/debugging/internal/demangle.cc | 478 +++++++++- .../absl/debugging/internal/demangle_rust.cc | 556 ++++++++++- .../absl/debugging/internal/demangle_rust.h | 6 +- .../debugging/internal/demangle_rust_test.cc | 368 ++++++++ .../absl/debugging/internal/demangle_test.cc | 649 +++++++++++++ .../abseil-cpp/absl/functional/overload.h | 2 +- .../absl/functional/overload_test.cc | 24 +- .../abseil-cpp/absl/hash/internal/hash.h | 4 +- third_party/abseil-cpp/absl/log/BUILD.bazel | 2 + .../abseil-cpp/absl/log/CMakeLists.txt | 4 + .../abseil-cpp/absl/log/check_test_impl.inc | 158 ++++ .../abseil-cpp/absl/log/internal/check_op.cc | 20 + .../abseil-cpp/absl/log/internal/check_op.h | 70 +- .../abseil-cpp/absl/random/BUILD.bazel | 16 +- .../abseil-cpp/absl/random/CMakeLists.txt | 36 +- .../absl/random/beta_distribution.h | 16 +- .../abseil-cpp/absl/random/distributions.h | 16 +- .../absl/random/distributions_test.cc | 60 +- .../absl/random/internal/BUILD.bazel | 16 +- .../absl/random/internal/mock_helpers.h | 40 +- .../absl/random/internal/mock_overload_set.h | 82 +- .../absl/random/internal/mock_validators.h | 98 ++ .../absl/random/mock_distributions.h | 19 +- .../absl/random/mock_distributions_test.cc | 215 +++++ .../abseil-cpp/absl/random/mocking_bit_gen.h | 158 ++-- .../absl/random/mocking_bit_gen_test.cc | 32 +- .../absl/strings/has_absl_stringify.h | 1 + .../absl/strings/internal/charconv_bigint.h | 10 + .../strings/internal/has_absl_stringify.h | 12 +- .../absl/strings/internal/str_join_internal.h | 10 + .../abseil-cpp/absl/strings/str_join.h | 24 +- .../absl/strings/str_join_benchmark.cc | 10 + .../abseil-cpp/absl/strings/str_join_test.cc | 36 + .../internal/kernel_timeout_test.cc | 2 +- .../synchronization/internal/waiter_test.cc | 2 +- .../abseil-cpp/ci/linux_docker_containers.sh | 4 +- .../ci/linux_gcc-latest_libstdcxx_cmake.sh | 55 +- .../abseil-cpp/ci/linux_gcc_alpine_cmake.sh | 54 +- .../abseil-cpp/ci/macos_xcode_cmake.sh | 43 +- .../abseil-cpp/ci/windows_msvc_cmake.bat | 1 - third_party/abseil-cpp/create_lts.py | 27 +- .../bazel-skylib/.bazelci/presubmit.yml | 1 + third_party/bazel-skylib/.bcr/config.yml | 4 + .../.bcr/gazelle/metadata.template.json | 15 + .../bazel-skylib/.bcr/gazelle/presubmit.yml | 17 + .../.bcr/gazelle/source.template.json | 5 + .../bazel-skylib/.bcr/metadata.template.json | 15 + third_party/bazel-skylib/.bcr/presubmit.yml | 18 + .../bazel-skylib/.bcr/source.template.json | 5 + third_party/bazel-skylib/.gitignore | 1 + third_party/bazel-skylib/CHANGELOG.md | 48 +- third_party/bazel-skylib/MODULE.bazel | 10 +- third_party/bazel-skylib/README.md | 4 + third_party/bazel-skylib/WORKSPACE | 10 + third_party/bazel-skylib/docs/BUILD | 24 + .../bazel-skylib/docs/directory_doc.md | 23 + .../bazel-skylib/docs/directory_glob_doc.md | 39 + .../docs/directory_providers_doc.md | 48 + .../docs/directory_subdirectory_doc.md | 24 + .../bazel-skylib/docs/directory_utils_doc.md | 146 +++ .../bazel-skylib/docs/maintainers_guide.md | 33 +- third_party/bazel-skylib/docs/paths_doc.md | 48 + third_party/bazel-skylib/gazelle/MODULE.bazel | 4 +- third_party/bazel-skylib/lib/paths.bzl | 78 ++ .../bazel-skylib/rules/directory/BUILD | 49 + .../rules/directory/directory.bzl | 141 +++ .../bazel-skylib/rules/directory/glob.bzl | 73 ++ .../rules/directory/private/BUILD | 26 + .../rules/directory/private/glob.bzl | 137 +++ .../rules/directory/private/paths.bzl | 94 ++ .../rules/directory/providers.bzl | 49 + .../rules/directory/subdirectory.bzl | 40 + .../bazel-skylib/tests/directory/BUILD | 40 + .../tests/directory/directory_test.bzl | 158 ++++ .../directory/external_directory_tests.bzl | 56 ++ .../tests/directory/glob_test.bzl | 184 ++++ .../tests/directory/subdirectory_test.bzl | 113 +++ .../bazel-skylib/tests/directory/testdata/f1 | 0 .../tests/directory/testdata/subdir/f2 | 0 .../bazel-skylib/tests/directory/utils.bzl | 65 ++ .../bazel-skylib/tests/paths_tests.bzl | 76 ++ .../bazel-skylib/tests/subpackages_tests.bzl | 2 + third_party/bazel-skylib/version.bzl | 2 +- third_party/fmt/.bazelversion | 2 +- third_party/fmt/.github/workflows/doc.yml | 6 +- third_party/fmt/.github/workflows/lint.yml | 2 +- third_party/fmt/.github/workflows/linux.yml | 7 +- third_party/fmt/.github/workflows/macos.yml | 5 +- .../fmt/.github/workflows/scorecard.yml | 4 +- third_party/fmt/.github/workflows/windows.yml | 4 +- third_party/fmt/.gitignore | 4 - third_party/fmt/BUILD.bazel | 4 + third_party/fmt/CMakeLists.txt | 41 +- third_party/fmt/MODULE.bazel | 2 + third_party/fmt/doc/CMakeLists.txt | 26 - third_party/fmt/doc/_static/bootstrap.min.js | 7 - third_party/fmt/doc/_static/breathe.css | 28 - .../fonts/glyphicons-halflings-regular.eot | Bin 20335 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 229 ----- .../fonts/glyphicons-halflings-regular.ttf | Bin 41280 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23320 -> 0 bytes third_party/fmt/doc/_templates/layout.html | 148 --- third_party/fmt/doc/_templates/search.html | 55 -- third_party/fmt/doc/api.md | 652 +++++++++++++ third_party/fmt/doc/api.rst | 688 -------------- third_party/fmt/doc/basic-bootstrap/README | 2 - .../fmt/doc/basic-bootstrap/layout.html | 208 ---- .../fmt/doc/basic-bootstrap/theme.conf | 2 - third_party/fmt/doc/bootstrap/alerts.less | 73 -- third_party/fmt/doc/bootstrap/badges.less | 66 -- third_party/fmt/doc/bootstrap/bootstrap.less | 50 - .../fmt/doc/bootstrap/breadcrumbs.less | 26 - .../fmt/doc/bootstrap/button-groups.less | 243 ----- third_party/fmt/doc/bootstrap/buttons.less | 160 ---- third_party/fmt/doc/bootstrap/carousel.less | 269 ------ third_party/fmt/doc/bootstrap/close.less | 34 - third_party/fmt/doc/bootstrap/code.less | 69 -- .../doc/bootstrap/component-animations.less | 33 - third_party/fmt/doc/bootstrap/dropdowns.less | 214 ----- third_party/fmt/doc/bootstrap/forms.less | 574 ----------- third_party/fmt/doc/bootstrap/glyphicons.less | 305 ------ third_party/fmt/doc/bootstrap/grid.less | 84 -- .../fmt/doc/bootstrap/input-groups.less | 166 ---- third_party/fmt/doc/bootstrap/jumbotron.less | 50 - third_party/fmt/doc/bootstrap/labels.less | 64 -- third_party/fmt/doc/bootstrap/list-group.less | 124 --- third_party/fmt/doc/bootstrap/media.less | 61 -- third_party/fmt/doc/bootstrap/mixins.less | 39 - .../fmt/doc/bootstrap/mixins/alerts.less | 14 - .../bootstrap/mixins/background-variant.less | 8 - .../doc/bootstrap/mixins/border-radius.less | 18 - .../fmt/doc/bootstrap/mixins/buttons.less | 52 - .../doc/bootstrap/mixins/center-block.less | 7 - .../fmt/doc/bootstrap/mixins/clearfix.less | 22 - .../fmt/doc/bootstrap/mixins/forms.less | 85 -- .../fmt/doc/bootstrap/mixins/gradients.less | 59 -- .../doc/bootstrap/mixins/grid-framework.less | 91 -- .../fmt/doc/bootstrap/mixins/grid.less | 122 --- .../fmt/doc/bootstrap/mixins/hide-text.less | 21 - .../fmt/doc/bootstrap/mixins/image.less | 33 - .../fmt/doc/bootstrap/mixins/labels.less | 12 - .../fmt/doc/bootstrap/mixins/list-group.less | 29 - .../fmt/doc/bootstrap/mixins/nav-divider.less | 10 - .../bootstrap/mixins/nav-vertical-align.less | 9 - .../fmt/doc/bootstrap/mixins/opacity.less | 8 - .../fmt/doc/bootstrap/mixins/pagination.less | 23 - .../fmt/doc/bootstrap/mixins/panels.less | 24 - .../doc/bootstrap/mixins/progress-bar.less | 10 - .../doc/bootstrap/mixins/reset-filter.less | 8 - .../fmt/doc/bootstrap/mixins/resize.less | 6 - .../mixins/responsive-visibility.less | 15 - .../fmt/doc/bootstrap/mixins/size.less | 10 - .../fmt/doc/bootstrap/mixins/tab-focus.less | 9 - .../fmt/doc/bootstrap/mixins/table-row.less | 28 - .../doc/bootstrap/mixins/text-emphasis.less | 8 - .../doc/bootstrap/mixins/text-overflow.less | 8 - .../doc/bootstrap/mixins/vendor-prefixes.less | 227 ----- third_party/fmt/doc/bootstrap/modals.less | 150 --- third_party/fmt/doc/bootstrap/navbar.less | 660 ------------- third_party/fmt/doc/bootstrap/navs.less | 242 ----- third_party/fmt/doc/bootstrap/normalize.less | 427 --------- third_party/fmt/doc/bootstrap/pager.less | 54 -- third_party/fmt/doc/bootstrap/pagination.less | 88 -- third_party/fmt/doc/bootstrap/panels.less | 265 ------ third_party/fmt/doc/bootstrap/popovers.less | 135 --- third_party/fmt/doc/bootstrap/print.less | 107 --- .../fmt/doc/bootstrap/progress-bars.less | 87 -- .../fmt/doc/bootstrap/responsive-embed.less | 35 - .../doc/bootstrap/responsive-utilities.less | 194 ---- .../fmt/doc/bootstrap/scaffolding.less | 162 ---- third_party/fmt/doc/bootstrap/tables.less | 234 ----- third_party/fmt/doc/bootstrap/theme.less | 273 ------ third_party/fmt/doc/bootstrap/thumbnails.less | 36 - third_party/fmt/doc/bootstrap/tooltip.less | 102 -- third_party/fmt/doc/bootstrap/type.less | 302 ------ third_party/fmt/doc/bootstrap/utilities.less | 55 -- third_party/fmt/doc/bootstrap/variables.less | 861 ----------------- third_party/fmt/doc/bootstrap/wells.less | 29 - third_party/fmt/doc/build.py | 126 --- third_party/fmt/doc/conf.py | 256 ----- third_party/fmt/doc/contents.rst | 10 - third_party/fmt/doc/fmt.css | 11 + third_party/fmt/doc/fmt.js | 4 + third_party/fmt/doc/fmt.less | 71 -- third_party/fmt/doc/index.md | 175 ++++ third_party/fmt/doc/index.rst | 198 ---- third_party/fmt/doc/syntax.md | 888 ++++++++++++++++++ third_party/fmt/doc/syntax.rst | 673 ------------- third_party/fmt/doc/usage.md | 242 +++++ third_party/fmt/doc/usage.rst | 250 ----- third_party/fmt/include/fmt/args.h | 105 +-- third_party/fmt/include/fmt/base.h | 328 +++---- third_party/fmt/include/fmt/chrono.h | 117 ++- third_party/fmt/include/fmt/color.h | 102 +- third_party/fmt/include/fmt/compile.h | 88 +- third_party/fmt/include/fmt/format-inl.h | 6 +- third_party/fmt/include/fmt/format.h | 236 ++--- third_party/fmt/include/fmt/os.h | 145 ++- third_party/fmt/include/fmt/ostream.h | 32 +- third_party/fmt/include/fmt/printf.h | 63 +- third_party/fmt/include/fmt/ranges.h | 85 +- third_party/fmt/include/fmt/std.h | 165 ++-- third_party/fmt/include/fmt/xchar.h | 16 +- third_party/fmt/support/bazel/README.md | 71 +- third_party/fmt/support/mkdocs.yml | 45 + .../mkdocstrings_handlers/cxx/__init__.py | 306 ++++++ .../cxx/templates/README | 1 + third_party/fmt/support/run-mkdocs | 14 + third_party/fmt/test/CMakeLists.txt | 3 + third_party/fmt/test/base-test.cc | 70 ++ third_party/fmt/test/chrono-test.cc | 22 +- third_party/fmt/test/ranges-test.cc | 57 +- third_party/fmt/test/std-test.cc | 7 + third_party/fmt/test/unicode-test.cc | 2 +- third_party/fmt/test/xchar-test.cc | 6 - third_party/googletest/ci/linux-presubmit.sh | 2 +- .../googletest/docs/reference/testing.md | 17 + .../googlemock/src/gmock-matchers.cc | 27 +- .../test/gmock-matchers-containers_test.cc | 17 +- .../include/gtest/gtest-death-test.h | 8 +- .../internal/gtest-death-test-internal.h | 45 +- .../googletest/googletest/src/gtest.cc | 12 +- .../workflows/python-wheels-publish-test.yml | 2 +- .../workflows/python-wheels-publish.yml | 2 +- .../.github/workflows/python-wheels.yml | 2 +- .../openexr/.github/workflows/scorecard.yml | 2 +- .../openexr/.github/workflows/website.yml | 17 +- third_party/openexr/CMakeLists.txt | 38 +- .../openexr/src/lib/OpenEXRCore/part_attr.c | 2 +- .../openexr/website/StandardAttributes.rst | 2 +- third_party/pugixml/docs/manual.adoc | 14 +- third_party/pugixml/docs/manual.html | 18 +- third_party/pugixml/src/pugixml.hpp | 6 +- third_party/software-bill-of-materials.md | 16 +- 252 files changed, 8866 insertions(+), 12872 deletions(-) create mode 100644 third_party/abseil-cpp/absl/random/internal/mock_validators.h create mode 100644 third_party/bazel-skylib/.bcr/config.yml create mode 100644 third_party/bazel-skylib/.bcr/gazelle/metadata.template.json create mode 100644 third_party/bazel-skylib/.bcr/gazelle/presubmit.yml create mode 100644 third_party/bazel-skylib/.bcr/gazelle/source.template.json create mode 100644 third_party/bazel-skylib/.bcr/metadata.template.json create mode 100644 third_party/bazel-skylib/.bcr/presubmit.yml create mode 100644 third_party/bazel-skylib/.bcr/source.template.json create mode 100755 third_party/bazel-skylib/docs/directory_doc.md create mode 100755 third_party/bazel-skylib/docs/directory_glob_doc.md create mode 100755 third_party/bazel-skylib/docs/directory_providers_doc.md create mode 100755 third_party/bazel-skylib/docs/directory_subdirectory_doc.md create mode 100755 third_party/bazel-skylib/docs/directory_utils_doc.md create mode 100644 third_party/bazel-skylib/rules/directory/BUILD create mode 100644 third_party/bazel-skylib/rules/directory/directory.bzl create mode 100644 third_party/bazel-skylib/rules/directory/glob.bzl create mode 100644 third_party/bazel-skylib/rules/directory/private/BUILD create mode 100644 third_party/bazel-skylib/rules/directory/private/glob.bzl create mode 100644 third_party/bazel-skylib/rules/directory/private/paths.bzl create mode 100644 third_party/bazel-skylib/rules/directory/providers.bzl create mode 100644 third_party/bazel-skylib/rules/directory/subdirectory.bzl create mode 100644 third_party/bazel-skylib/tests/directory/BUILD create mode 100644 third_party/bazel-skylib/tests/directory/directory_test.bzl create mode 100644 third_party/bazel-skylib/tests/directory/external_directory_tests.bzl create mode 100644 third_party/bazel-skylib/tests/directory/glob_test.bzl create mode 100644 third_party/bazel-skylib/tests/directory/subdirectory_test.bzl create mode 100644 third_party/bazel-skylib/tests/directory/testdata/f1 create mode 100644 third_party/bazel-skylib/tests/directory/testdata/subdir/f2 create mode 100644 third_party/bazel-skylib/tests/directory/utils.bzl delete mode 100644 third_party/fmt/doc/CMakeLists.txt delete mode 100644 third_party/fmt/doc/_static/bootstrap.min.js delete mode 100644 third_party/fmt/doc/_static/breathe.css delete mode 100644 third_party/fmt/doc/_static/fonts/glyphicons-halflings-regular.eot delete mode 100644 third_party/fmt/doc/_static/fonts/glyphicons-halflings-regular.svg delete mode 100644 third_party/fmt/doc/_static/fonts/glyphicons-halflings-regular.ttf delete mode 100644 third_party/fmt/doc/_static/fonts/glyphicons-halflings-regular.woff delete mode 100644 third_party/fmt/doc/_templates/layout.html delete mode 100644 third_party/fmt/doc/_templates/search.html create mode 100644 third_party/fmt/doc/api.md delete mode 100644 third_party/fmt/doc/api.rst delete mode 100644 third_party/fmt/doc/basic-bootstrap/README delete mode 100644 third_party/fmt/doc/basic-bootstrap/layout.html delete mode 100644 third_party/fmt/doc/basic-bootstrap/theme.conf delete mode 100644 third_party/fmt/doc/bootstrap/alerts.less delete mode 100644 third_party/fmt/doc/bootstrap/badges.less delete mode 100644 third_party/fmt/doc/bootstrap/bootstrap.less delete mode 100644 third_party/fmt/doc/bootstrap/breadcrumbs.less delete mode 100644 third_party/fmt/doc/bootstrap/button-groups.less delete mode 100644 third_party/fmt/doc/bootstrap/buttons.less delete mode 100644 third_party/fmt/doc/bootstrap/carousel.less delete mode 100644 third_party/fmt/doc/bootstrap/close.less delete mode 100644 third_party/fmt/doc/bootstrap/code.less delete mode 100644 third_party/fmt/doc/bootstrap/component-animations.less delete mode 100644 third_party/fmt/doc/bootstrap/dropdowns.less delete mode 100644 third_party/fmt/doc/bootstrap/forms.less delete mode 100644 third_party/fmt/doc/bootstrap/glyphicons.less delete mode 100644 third_party/fmt/doc/bootstrap/grid.less delete mode 100644 third_party/fmt/doc/bootstrap/input-groups.less delete mode 100644 third_party/fmt/doc/bootstrap/jumbotron.less delete mode 100644 third_party/fmt/doc/bootstrap/labels.less delete mode 100644 third_party/fmt/doc/bootstrap/list-group.less delete mode 100644 third_party/fmt/doc/bootstrap/media.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/alerts.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/background-variant.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/border-radius.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/buttons.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/center-block.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/clearfix.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/forms.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/gradients.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/grid-framework.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/grid.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/hide-text.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/image.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/labels.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/list-group.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/nav-divider.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/nav-vertical-align.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/opacity.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/pagination.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/panels.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/progress-bar.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/reset-filter.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/resize.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/responsive-visibility.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/size.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/tab-focus.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/table-row.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/text-emphasis.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/text-overflow.less delete mode 100644 third_party/fmt/doc/bootstrap/mixins/vendor-prefixes.less delete mode 100644 third_party/fmt/doc/bootstrap/modals.less delete mode 100644 third_party/fmt/doc/bootstrap/navbar.less delete mode 100644 third_party/fmt/doc/bootstrap/navs.less delete mode 100644 third_party/fmt/doc/bootstrap/normalize.less delete mode 100644 third_party/fmt/doc/bootstrap/pager.less delete mode 100644 third_party/fmt/doc/bootstrap/pagination.less delete mode 100644 third_party/fmt/doc/bootstrap/panels.less delete mode 100644 third_party/fmt/doc/bootstrap/popovers.less delete mode 100644 third_party/fmt/doc/bootstrap/print.less delete mode 100644 third_party/fmt/doc/bootstrap/progress-bars.less delete mode 100644 third_party/fmt/doc/bootstrap/responsive-embed.less delete mode 100644 third_party/fmt/doc/bootstrap/responsive-utilities.less delete mode 100644 third_party/fmt/doc/bootstrap/scaffolding.less delete mode 100644 third_party/fmt/doc/bootstrap/tables.less delete mode 100644 third_party/fmt/doc/bootstrap/theme.less delete mode 100644 third_party/fmt/doc/bootstrap/thumbnails.less delete mode 100644 third_party/fmt/doc/bootstrap/tooltip.less delete mode 100644 third_party/fmt/doc/bootstrap/type.less delete mode 100644 third_party/fmt/doc/bootstrap/utilities.less delete mode 100644 third_party/fmt/doc/bootstrap/variables.less delete mode 100644 third_party/fmt/doc/bootstrap/wells.less delete mode 100755 third_party/fmt/doc/build.py delete mode 100644 third_party/fmt/doc/conf.py delete mode 100644 third_party/fmt/doc/contents.rst create mode 100644 third_party/fmt/doc/fmt.css create mode 100644 third_party/fmt/doc/fmt.js delete mode 100644 third_party/fmt/doc/fmt.less create mode 100644 third_party/fmt/doc/index.md delete mode 100644 third_party/fmt/doc/index.rst create mode 100644 third_party/fmt/doc/syntax.md delete mode 100644 third_party/fmt/doc/syntax.rst create mode 100644 third_party/fmt/doc/usage.md delete mode 100644 third_party/fmt/doc/usage.rst create mode 100644 third_party/fmt/support/mkdocs.yml create mode 100644 third_party/fmt/support/mkdocstrings_handlers/cxx/__init__.py create mode 100644 third_party/fmt/support/mkdocstrings_handlers/cxx/templates/README create mode 100755 third_party/fmt/support/run-mkdocs diff --git a/devertexwahn/.bazelrc b/devertexwahn/.bazelrc index 9ef7c19a..f930774c 100644 --- a/devertexwahn/.bazelrc +++ b/devertexwahn/.bazelrc @@ -116,24 +116,26 @@ build:mingw --cxxopt=-std=c++2a #build:mingw --cxxopt=-Wextra # Visual Studio 2019 +#build:vs2019 --copt=-DNOGDI +#build:vs2019 --host_copt=-DNOGDI +build:vs2019 --copt=-DWIN32_LEAN_AND_MEAN +build:vs2019 --cxxopt=/utf-8 build:vs2019 --cxxopt=/std:c++20 build:vs2019 --cxxopt=/Zc:__cplusplus +build:vs2019 --define compiler=vs2019 build:vs2019 --enable_runfiles # https://github.com/bazelbuild/bazel/issues/8843 -build:vs2019 --copt=-DWIN32_LEAN_AND_MEAN -#build:vs2019 --copt=-DNOGDI build:vs2019 --host_copt=-DWIN32_LEAN_AND_MEAN -#build:vs2019 --host_copt=-DNOGDI -build:vs2019 --define compiler=vs2019 # Visual Studio 2022 +#build:vs2022 --copt=-DNOGDI +#build:vs2022 --host_copt=-DNOGDI +build:vs2022 --copt=-DWIN32_LEAN_AND_MEAN build:vs2022 --cxxopt=/std:c++20 +build:vs2022 --cxxopt=/utf-8 build:vs2022 --cxxopt=/Zc:__cplusplus +build:vs2022 --define compiler=vs2022 build:vs2022 --enable_runfiles # https://github.com/bazelbuild/bazel/issues/8843 -build:vs2022 --copt=-DWIN32_LEAN_AND_MEAN -#build:vs2022 --copt=-DNOGDI build:vs2022 --host_copt=-DWIN32_LEAN_AND_MEAN -#build:vs2022 --host_copt=-DNOGDI -build:vs2022 --define compiler=vs2022 build:windows --config=vs2022 diff --git a/devertexwahn/.bazelversion b/devertexwahn/.bazelversion index 58cbedfa..a8a18875 100644 --- a/devertexwahn/.bazelversion +++ b/devertexwahn/.bazelversion @@ -1 +1 @@ -7.2.0rc1 +7.1.2 diff --git a/devertexwahn/MODULE.bazel b/devertexwahn/MODULE.bazel index d5d7a8d2..a47f3858 100644 --- a/devertexwahn/MODULE.bazel +++ b/devertexwahn/MODULE.bazel @@ -7,7 +7,7 @@ module( bazel_dep(name = "abseil-cpp", version = "20240116.2") bazel_dep(name = "apple_support", version = "1.15.1") -bazel_dep(name = "bazel_skylib", version = "1.6.1") +bazel_dep(name = "bazel_skylib", version = "1.7.0") bazel_dep(name = "boost.algorithm", version = "1.83.0.bcr.1") bazel_dep(name = "boost.asio", version = "1.83.0.bcr.1") bazel_dep(name = "boost.core", version = "1.83.0.bcr.1") @@ -15,10 +15,8 @@ bazel_dep(name = "boost.predef", version = "1.83.0.bcr.1") bazel_dep(name = "boost.program_options", version = "1.83.0.bcr.1") bazel_dep(name = "boost.uuid", version = "1.83.0.bcr.1") bazel_dep(name = "eigen", version = "3.4.0") -bazel_dep(name = "embree", version = "4.3.1") -bazel_dep(name = "flip", version = "1.3") bazel_dep(name = "fmt", version = "10.2.1") -bazel_dep(name = "gazelle", version = "0.36.0") +bazel_dep(name = "gazelle", version = "0.37.0") bazel_dep(name = "glog", version = "0.7.0") bazel_dep(name = "hypothesis") bazel_dep(name = "imath", version = "3.1.11") diff --git a/third_party/abseil-cpp/CMake/AbseilDll.cmake b/third_party/abseil-cpp/CMake/AbseilDll.cmake index 2a40532a..828cc347 100644 --- a/third_party/abseil-cpp/CMake/AbseilDll.cmake +++ b/third_party/abseil-cpp/CMake/AbseilDll.cmake @@ -440,6 +440,43 @@ set(ABSL_INTERNAL_DLL_FILES "debugging/leak_check.cc" ) +if(NOT MSVC) + list(APPEND ABSL_INTERNAL_DLL_FILES + "flags/commandlineflag.cc" + "flags/commandlineflag.h" + "flags/config.h" + "flags/declare.h" + "flags/flag.h" + "flags/internal/commandlineflag.cc" + "flags/internal/commandlineflag.h" + "flags/internal/flag.cc" + "flags/internal/flag.h" + "flags/internal/parse.h" + "flags/internal/path_util.h" + "flags/internal/private_handle_accessor.cc" + "flags/internal/private_handle_accessor.h" + "flags/internal/program_name.cc" + "flags/internal/program_name.h" + "flags/internal/registry.h" + "flags/internal/sequence_lock.h" + "flags/internal/usage.cc" + "flags/internal/usage.h" + "flags/marshalling.cc" + "flags/marshalling.h" + "flags/parse.cc" + "flags/parse.h" + "flags/reflection.cc" + "flags/reflection.h" + "flags/usage.cc" + "flags/usage.h" + "flags/usage_config.cc" + "flags/usage_config.h" + "log/flags.cc" + "log/flags.h" + "log/internal/flags.h" + ) +endif() + set(ABSL_INTERNAL_DLL_TARGETS "absl_check" "absl_log" @@ -508,6 +545,7 @@ set(ABSL_INTERNAL_DLL_TARGETS "log_internal_check_op" "log_internal_conditions" "log_internal_config" + "log_internal_fnmatch" "log_internal_format" "log_internal_globals" "log_internal_log_impl" @@ -587,6 +625,7 @@ set(ABSL_INTERNAL_DLL_TARGETS "strerror" "strings" "strings_internal" + "string_view" "symbolize" "synchronization" "thread_pool" @@ -597,8 +636,29 @@ set(ABSL_INTERNAL_DLL_TARGETS "type_traits" "utility" "variant" + "vlog_config_internal" ) +if(NOT MSVC) + list(APPEND ABSL_INTERNAL_DLL_TARGETS + "flags" + "flags_commandlineflag" + "flags_commandlineflag_internal" + "flags_config" + "flags_internal" + "flags_marshalling" + "flags_parse" + "flags_path_util" + "flags_private_handle_accessor" + "flags_program_name" + "flags_reflection" + "flags_usage" + "flags_usage_internal" + "log_internal_flags" + "log_flags" + ) +endif() + set(ABSL_INTERNAL_TEST_DLL_FILES "hash/hash_testing.h" "log/scoped_mock_log.cc" @@ -675,12 +735,7 @@ function(absl_internal_dll_contains) STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_DLL_TARGET}) - list(FIND - ABSL_INTERNAL_DLL_TARGETS - "${_target}" - _index) - - if (${_index} GREATER -1) + if (_target IN_LIST ABSL_INTERNAL_DLL_TARGETS) set(${ABSL_INTERNAL_DLL_OUTPUT} 1 PARENT_SCOPE) else() set(${ABSL_INTERNAL_DLL_OUTPUT} 0 PARENT_SCOPE) @@ -697,12 +752,7 @@ function(absl_internal_test_dll_contains) STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_TEST_DLL_TARGET}) - list(FIND - ABSL_INTERNAL_TEST_DLL_TARGETS - "${_target}" - _index) - - if (${_index} GREATER -1) + if (_target IN_LIST ABSL_INTERNAL_TEST_DLL_TARGETS) set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 1 PARENT_SCOPE) else() set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 0 PARENT_SCOPE) @@ -754,7 +804,12 @@ function(absl_make_dll) else() set(_dll "abseil_dll") set(_dll_files ${ABSL_INTERNAL_DLL_FILES}) - set(_dll_libs "") + set(_dll_libs + Threads::Threads + # TODO(#1495): Use $ once our + # minimum CMake version >= 3.24 + $<$:-Wl,-framework,CoreFoundation> + ) set(_dll_compile_definitions "") set(_dll_includes "") set(_dll_consume "ABSL_CONSUME_DLL") @@ -772,7 +827,10 @@ function(absl_make_dll) ${_dll_libs} ${ABSL_DEFAULT_LINKOPTS} ) - set_property(TARGET ${_dll} PROPERTY LINKER_LANGUAGE "CXX") + set_target_properties(${_dll} PROPERTIES + LINKER_LANGUAGE "CXX" + SOVERSION ${ABSL_SOVERSION} + ) target_include_directories( ${_dll} PUBLIC diff --git a/third_party/abseil-cpp/CMake/AbseilHelpers.cmake b/third_party/abseil-cpp/CMake/AbseilHelpers.cmake index 44f5bb3f..70a37f11 100644 --- a/third_party/abseil-cpp/CMake/AbseilHelpers.cmake +++ b/third_party/abseil-cpp/CMake/AbseilHelpers.cmake @@ -306,7 +306,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n") if(ABSL_ENABLE_INSTALL) set_target_properties(${_NAME} PROPERTIES OUTPUT_NAME "absl_${_NAME}" - SOVERSION 0 + SOVERSION "${ABSL_SOVERSION}" ) endif() else() diff --git a/third_party/abseil-cpp/CMakeLists.txt b/third_party/abseil-cpp/CMakeLists.txt index 87f8ae82..d88283b2 100644 --- a/third_party/abseil-cpp/CMakeLists.txt +++ b/third_party/abseil-cpp/CMakeLists.txt @@ -59,6 +59,7 @@ if (POLICY CMP0141) endif (POLICY CMP0141) project(absl LANGUAGES CXX) +set(ABSL_SOVERSION 0) include(CTest) # Output directory is correct by default for most build setups. However, when @@ -149,6 +150,14 @@ set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH "If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout." ) +option(ABSL_BUILD_MONOLITHIC_SHARED_LIBS + "Build Abseil as a single shared library (always enabled for Windows)" + OFF +) +if(NOT BUILD_SHARED_LIBS AND ABSL_BUILD_MONOLITHIC_SHARED_LIBS) + message(WARNING "Not building a shared library because BUILD_SHARED_LIBS is not set. Ignoring ABSL_BUILD_MONOLITHIC_SHARED_LIBS.") +endif() + if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS) if (ABSL_USE_EXTERNAL_GOOGLETEST) if (ABSL_FIND_GOOGLETEST) diff --git a/third_party/abseil-cpp/absl/CMakeLists.txt b/third_party/abseil-cpp/absl/CMakeLists.txt index 3a7c12fe..810d7f31 100644 --- a/third_party/abseil-cpp/absl/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/CMakeLists.txt @@ -36,9 +36,9 @@ add_subdirectory(time) add_subdirectory(types) add_subdirectory(utility) -if (${ABSL_BUILD_DLL}) +if (ABSL_BUILD_DLL) absl_make_dll() - if (${ABSL_BUILD_TEST_HELPERS}) + if ((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS) absl_make_dll(TEST ON) endif() endif() diff --git a/third_party/abseil-cpp/absl/algorithm/container.h b/third_party/abseil-cpp/absl/algorithm/container.h index c7bafae1..a2d126b7 100644 --- a/third_party/abseil-cpp/absl/algorithm/container.h +++ b/third_party/abseil-cpp/absl/algorithm/container.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include diff --git a/third_party/abseil-cpp/absl/base/attributes.h b/third_party/abseil-cpp/absl/base/attributes.h index a6c40a76..e781995e 100644 --- a/third_party/abseil-cpp/absl/base/attributes.h +++ b/third_party/abseil-cpp/absl/base/attributes.h @@ -821,6 +821,32 @@ #define ABSL_ATTRIBUTE_LIFETIME_BOUND #endif +// ABSL_INTERNAL_ATTRIBUTE_VIEW indicates that a type acts like a view i.e. a +// raw (non-owning) pointer. This enables diagnoses similar to those enabled by +// ABSL_ATTRIBUTE_LIFETIME_BOUND. +// +// See the following links for details: +// https://reviews.llvm.org/D64448 +// https://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html +#if ABSL_HAVE_CPP_ATTRIBUTE(gsl::Pointer) +#define ABSL_INTERNAL_ATTRIBUTE_VIEW [[gsl::Pointer]] +#else +#define ABSL_INTERNAL_ATTRIBUTE_VIEW +#endif + +// ABSL_INTERNAL_ATTRIBUTE_OWNER indicates that a type acts like a smart +// (owning) pointer. This enables diagnoses similar to those enabled by +// ABSL_ATTRIBUTE_LIFETIME_BOUND. +// +// See the following links for details: +// https://reviews.llvm.org/D64448 +// https://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html +#if ABSL_HAVE_CPP_ATTRIBUTE(gsl::Owner) +#define ABSL_INTERNAL_ATTRIBUTE_OWNER [[gsl::Owner]] +#else +#define ABSL_INTERNAL_ATTRIBUTE_OWNER +#endif + // ABSL_ATTRIBUTE_TRIVIAL_ABI // Indicates that a type is "trivially relocatable" -- meaning it can be // relocated without invoking the constructor/destructor, using a form of move diff --git a/third_party/abseil-cpp/absl/base/config.h b/third_party/abseil-cpp/absl/base/config.h index 5fa9f0ef..a8f2eab5 100644 --- a/third_party/abseil-cpp/absl/base/config.h +++ b/third_party/abseil-cpp/absl/base/config.h @@ -231,12 +231,11 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. -// We assume __thread is supported on Linux or Asylo when compiled with Clang or +// We assume __thread is supported on Linux when compiled with Clang or // compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined. #ifdef ABSL_HAVE_TLS #error ABSL_HAVE_TLS cannot be directly set -#elif (defined(__linux__) || defined(__ASYLO__)) && \ - (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) +#elif (defined(__linux__)) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) #define ABSL_HAVE_TLS 1 #endif @@ -275,53 +274,18 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE 1 #endif + // ABSL_HAVE_THREAD_LOCAL // +// DEPRECATED - `thread_local` is available on all supported platforms. // Checks whether C++11's `thread_local` storage duration specifier is // supported. #ifdef ABSL_HAVE_THREAD_LOCAL #error ABSL_HAVE_THREAD_LOCAL cannot be directly set -#elif defined(__APPLE__) -// Notes: -// * Xcode's clang did not support `thread_local` until version 8, and -// even then not for all iOS < 9.0. -// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator -// targeting iOS 9.x. -// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time -// making ABSL_HAVE_FEATURE unreliable there. -// -#if ABSL_HAVE_FEATURE(cxx_thread_local) && \ - !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) -#define ABSL_HAVE_THREAD_LOCAL 1 -#endif -#else // !defined(__APPLE__) +#else #define ABSL_HAVE_THREAD_LOCAL 1 #endif -// There are platforms for which TLS should not be used even though the compiler -// makes it seem like it's supported (Android NDK < r12b for example). -// This is primarily because of linker problems and toolchain misconfiguration: -// Abseil does not intend to support this indefinitely. Currently, the newest -// toolchain that we intend to support that requires this behavior is the -// r11 NDK - allowing for a 5 year support window on that means this option -// is likely to be removed around June of 2021. -// TLS isn't supported until NDK r12b per -// https://developer.android.com/ndk/downloads/revision_history.html -// Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in -// . For NDK < r16, users should define these macros, -// e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. -#if defined(__ANDROID__) && defined(__clang__) -#if __has_include() -#include -#endif // __has_include() -#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \ - defined(__NDK_MINOR__) && \ - ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1))) -#undef ABSL_HAVE_TLS -#undef ABSL_HAVE_THREAD_LOCAL -#endif -#endif // defined(__ANDROID__) && defined(__clang__) - // ABSL_HAVE_INTRINSIC_INT128 // // Checks whether the __int128 compiler extension for a 128-bit integral type is @@ -414,9 +378,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ defined(__asmjs__) || defined(__EMSCRIPTEN__) || defined(__Fuchsia__) || \ - defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ - defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ - defined(__QNX__) || defined(__VXWORKS__) || defined(__hexagon__) + defined(__sun) || defined(__myriad2__) || defined(__HAIKU__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) || defined(__QNX__) || \ + defined(__VXWORKS__) || defined(__hexagon__) #define ABSL_HAVE_MMAP 1 #endif @@ -900,7 +864,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #error ABSL_INTERNAL_HAS_CXA_DEMANGLE cannot be directly set #elif defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__)) #define ABSL_INTERNAL_HAS_CXA_DEMANGLE 0 -#elif defined(__GNUC__) && !defined(__mips__) +#elif defined(__GNUC__) #define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1 #elif defined(__clang__) && !defined(_MSC_VER) #define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1 diff --git a/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc b/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc index 49818fb8..3cd9e18b 100644 --- a/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc @@ -15,8 +15,11 @@ #include "absl/container/internal/compressed_tuple.h" #include +#include #include +#include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -55,6 +58,7 @@ namespace { using absl::test_internal::CopyableMovableInstance; using absl::test_internal::InstanceTracker; +using ::testing::Each; TEST(CompressedTupleTest, Sizeof) { EXPECT_EQ(sizeof(int), sizeof(CompressedTuple)); @@ -71,6 +75,30 @@ TEST(CompressedTupleTest, Sizeof) { sizeof(CompressedTuple, NotEmpty, Empty<1>>)); } +TEST(CompressedTupleTest, PointerToEmpty) { + auto to_void_ptrs = [](const auto&... objs) { + return std::vector{static_cast(&objs)...}; + }; + { + using Tuple = CompressedTuple>; + EXPECT_EQ(sizeof(int), sizeof(Tuple)); + Tuple t; + EXPECT_THAT(to_void_ptrs(t.get<1>()), Each(&t)); + } + { + using Tuple = CompressedTuple, Empty<1>>; + EXPECT_EQ(sizeof(int), sizeof(Tuple)); + Tuple t; + EXPECT_THAT(to_void_ptrs(t.get<1>(), t.get<2>()), Each(&t)); + } + { + using Tuple = CompressedTuple, Empty<1>, Empty<2>>; + EXPECT_EQ(sizeof(int), sizeof(Tuple)); + Tuple t; + EXPECT_THAT(to_void_ptrs(t.get<1>(), t.get<2>(), t.get<3>()), Each(&t)); + } +} + TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) { InstanceTracker tracker; CompressedTuple x1(CopyableMovableInstance(1)); diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc index c23c1f3b..1cae0381 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc @@ -23,7 +23,10 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/endian.h" +#include "absl/base/optimization.h" #include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hashtablez_sampler.h" #include "absl/hash/hash.h" namespace absl { @@ -157,6 +160,8 @@ FindInfo find_first_non_full_outofline(const CommonFields& common, return find_first_non_full(common, hash); } +namespace { + // Returns the address of the slot just after slot assuming each slot has the // specified size. static inline void* NextSlot(void* slot, size_t slot_size) { @@ -169,8 +174,22 @@ static inline void* PrevSlot(void* slot, size_t slot_size) { return reinterpret_cast(reinterpret_cast(slot) - slot_size); } -void DropDeletesWithoutResize(CommonFields& common, const void* hash_fn, - const PolicyFunctions& policy, void* tmp_space) { +// Finds guaranteed to exists empty slot from the given position. +// NOTE: this function is almost never triggered inside of the +// DropDeletesWithoutResize, so we keep it simple. +// The table is rather sparse, so empty slot will be found very quickly. +size_t FindEmptySlot(size_t start, size_t end, const ctrl_t* ctrl) { + for (size_t i = start; i < end; ++i) { + if (IsEmpty(ctrl[i])) { + return i; + } + } + assert(false && "no empty slot"); + return ~size_t{}; +} + +void DropDeletesWithoutResize(CommonFields& common, + const PolicyFunctions& policy) { void* set = &common; void* slot_array = common.slot_array(); const size_t capacity = common.capacity(); @@ -194,15 +213,26 @@ void DropDeletesWithoutResize(CommonFields& common, const void* hash_fn, // repeat procedure for current slot with moved from element (target) ctrl_t* ctrl = common.control(); ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity); + const void* hash_fn = policy.hash_fn(common); auto hasher = policy.hash_slot; auto transfer = policy.transfer; const size_t slot_size = policy.slot_size; size_t total_probe_length = 0; void* slot_ptr = SlotAddress(slot_array, 0, slot_size); + + // The index of an empty slot that can be used as temporary memory for + // the swap operation. + constexpr size_t kUnknownId = ~size_t{}; + size_t tmp_space_id = kUnknownId; + for (size_t i = 0; i != capacity; ++i, slot_ptr = NextSlot(slot_ptr, slot_size)) { assert(slot_ptr == SlotAddress(slot_array, i, slot_size)); + if (IsEmpty(ctrl[i])) { + tmp_space_id = i; + continue; + } if (!IsDeleted(ctrl[i])) continue; const size_t hash = (*hasher)(hash_fn, slot_ptr); const FindInfo target = find_first_non_full(common, hash); @@ -231,16 +261,26 @@ void DropDeletesWithoutResize(CommonFields& common, const void* hash_fn, SetCtrl(common, new_i, H2(hash), slot_size); (*transfer)(set, new_slot_ptr, slot_ptr); SetCtrl(common, i, ctrl_t::kEmpty, slot_size); + // Initialize or change empty space id. + tmp_space_id = i; } else { assert(IsDeleted(ctrl[new_i])); SetCtrl(common, new_i, H2(hash), slot_size); // Until we are done rehashing, DELETED marks previously FULL slots. + if (tmp_space_id == kUnknownId) { + tmp_space_id = FindEmptySlot(i + 1, capacity, ctrl); + } + void* tmp_space = SlotAddress(slot_array, tmp_space_id, slot_size); + SanitizerUnpoisonMemoryRegion(tmp_space, slot_size); + // Swap i and new_i elements. (*transfer)(set, tmp_space, new_slot_ptr); (*transfer)(set, new_slot_ptr, slot_ptr); (*transfer)(set, slot_ptr, tmp_space); + SanitizerPoisonMemoryRegion(tmp_space, slot_size); + // repeat the processing of the ith slot --i; slot_ptr = PrevSlot(slot_ptr, slot_size); @@ -267,6 +307,8 @@ static bool WasNeverFull(CommonFields& c, size_t index) { Group::kWidth; } +} // namespace + void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) { assert(IsFull(c.control()[index]) && "erasing a dangling iterator"); c.decrement_size(); @@ -301,77 +343,126 @@ void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy, } void HashSetResizeHelper::GrowIntoSingleGroupShuffleControlBytes( - ctrl_t* new_ctrl, size_t new_capacity) const { + ctrl_t* __restrict new_ctrl, size_t new_capacity) const { assert(is_single_group(new_capacity)); constexpr size_t kHalfWidth = Group::kWidth / 2; + constexpr size_t kQuarterWidth = Group::kWidth / 4; assert(old_capacity_ < kHalfWidth); + static_assert(sizeof(uint64_t) >= kHalfWidth, + "Group size is too large. The ctrl bytes for half a group must " + "fit into a uint64_t for this implementation."); + static_assert(sizeof(uint64_t) <= Group::kWidth, + "Group size is too small. The ctrl bytes for a group must " + "cover a uint64_t for this implementation."); const size_t half_old_capacity = old_capacity_ / 2; // NOTE: operations are done with compile time known size = kHalfWidth. // Compiler optimizes that into single ASM operation. - // Copy second half of bytes to the beginning. - // We potentially copy more bytes in order to have compile time known size. - // Mirrored bytes from the old_ctrl() will also be copied. - // In case of old_capacity_ == 3, we will copy 1st element twice. + // Load the bytes from half_old_capacity + 1. This contains the last half of + // old_ctrl bytes, followed by the sentinel byte, and then the first half of + // the cloned bytes. This effectively shuffles the control bytes. + uint64_t copied_bytes = 0; + copied_bytes = + absl::little_endian::Load64(old_ctrl() + half_old_capacity + 1); + + // We change the sentinel byte to kEmpty before storing to both the start of + // the new_ctrl, and past the end of the new_ctrl later for the new cloned + // bytes. Note that this is faster than setting the sentinel byte to kEmpty + // after the copy directly in new_ctrl because we are limited on store + // bandwidth. + constexpr uint64_t kEmptyXorSentinel = + static_cast(ctrl_t::kEmpty) ^ + static_cast(ctrl_t::kSentinel); + const uint64_t mask_convert_old_sentinel_to_empty = + kEmptyXorSentinel << (half_old_capacity * 8); + copied_bytes ^= mask_convert_old_sentinel_to_empty; + + // Copy second half of bytes to the beginning. This correctly sets the bytes + // [0, old_capacity]. We potentially copy more bytes in order to have compile + // time known size. Mirrored bytes from the old_ctrl() will also be copied. In + // case of old_capacity_ == 3, we will copy 1st element twice. // Examples: + // (old capacity = 1) // old_ctrl = 0S0EEEEEEE... - // new_ctrl = S0EEEEEEEE... + // new_ctrl = E0EEEEEE??... // - // old_ctrl = 01S01EEEEE... - // new_ctrl = 1S01EEEEEE... + // (old capacity = 3) + // old_ctrl = 012S012EEEEE... + // new_ctrl = 12E012EE????... // + // (old capacity = 7) // old_ctrl = 0123456S0123456EE... - // new_ctrl = 456S0123?????????... - std::memcpy(new_ctrl, old_ctrl() + half_old_capacity + 1, kHalfWidth); - // Clean up copied kSentinel from old_ctrl. - new_ctrl[half_old_capacity] = ctrl_t::kEmpty; - - // Clean up damaged or uninitialized bytes. - - // Clean bytes after the intended size of the copy. - // Example: - // new_ctrl = 1E01EEEEEEE???? - // *new_ctrl= 1E0EEEEEEEE???? - // position / - std::memset(new_ctrl + old_capacity_ + 1, static_cast(ctrl_t::kEmpty), - kHalfWidth); - // Clean non-mirrored bytes that are not initialized. - // For small old_capacity that may be inside of mirrored bytes zone. + // new_ctrl = 456E0123?????????... + absl::little_endian::Store64(new_ctrl, copied_bytes); + + // Set the space [old_capacity + 1, new_capacity] to empty as these bytes will + // not be written again. This is safe because + // NumControlBytes = new_capacity + kWidth and new_capacity >= + // old_capacity+1. // Examples: - // new_ctrl = 1E0EEEEEEEE??????????.... - // *new_ctrl= 1E0EEEEEEEEEEEEE?????.... - // position / + // (old_capacity = 3, new_capacity = 15) + // new_ctrl = 12E012EE?????????????...?? + // *new_ctrl = 12E0EEEEEEEEEEEEEEEE?...?? + // position / S // - // new_ctrl = 456E0123???????????... - // *new_ctrl= 456E0123EEEEEEEE???... - // position / - std::memset(new_ctrl + kHalfWidth, static_cast(ctrl_t::kEmpty), - kHalfWidth); - // Clean last mirrored bytes that are not initialized - // and will not be overwritten by mirroring. + // (old_capacity = 7, new_capacity = 15) + // new_ctrl = 456E0123?????????????????...?? + // *new_ctrl = 456E0123EEEEEEEEEEEEEEEE?...?? + // position / S + std::memset(new_ctrl + old_capacity_ + 1, static_cast(ctrl_t::kEmpty), + Group::kWidth); + + // Set the last kHalfWidth bytes to empty, to ensure the bytes all the way to + // the end are initialized. // Examples: - // new_ctrl = 1E0EEEEEEEEEEEEE???????? - // *new_ctrl= 1E0EEEEEEEEEEEEEEEEEEEEE - // position S / + // new_ctrl = 12E0EEEEEEEEEEEEEEEE?...??????? + // *new_ctrl = 12E0EEEEEEEEEEEEEEEE???EEEEEEEE + // position S / // - // new_ctrl = 456E0123EEEEEEEE??????????????? - // *new_ctrl= 456E0123EEEEEEEE???????EEEEEEEE - // position S / - std::memset(new_ctrl + new_capacity + kHalfWidth, + // new_ctrl = 456E0123EEEEEEEEEEEEEEEE??????? + // *new_ctrl = 456E0123EEEEEEEEEEEEEEEEEEEEEEE + // position S / + std::memset(new_ctrl + NumControlBytes(new_capacity) - kHalfWidth, static_cast(ctrl_t::kEmpty), kHalfWidth); - // Create mirrored bytes. old_capacity_ < kHalfWidth - // Example: - // new_ctrl = 456E0123EEEEEEEE???????EEEEEEEE - // *new_ctrl= 456E0123EEEEEEEE456E0123EEEEEEE - // position S/ - ctrl_t g[kHalfWidth]; - std::memcpy(g, new_ctrl, kHalfWidth); - std::memcpy(new_ctrl + new_capacity + 1, g, kHalfWidth); + // Copy the first bytes to the end (starting at new_capacity +1) to set the + // cloned bytes. Note that we use the already copied bytes from old_ctrl here + // rather than copying from new_ctrl to avoid a Read-after-Write hazard, since + // new_ctrl was just written to. The first old_capacity-1 bytes are set + // correctly. Then there may be up to old_capacity bytes that need to be + // overwritten, and any remaining bytes will be correctly set to empty. This + // sets [new_capacity + 1, new_capacity +1 + old_capacity] correctly. + // Examples: + // new_ctrl = 12E0EEEEEEEEEEEEEEEE?...??????? + // *new_ctrl = 12E0EEEEEEEEEEEE12E012EEEEEEEEE + // position S/ + // + // new_ctrl = 456E0123EEEEEEEE?...???EEEEEEEE + // *new_ctrl = 456E0123EEEEEEEE456E0123EEEEEEE + // position S/ + absl::little_endian::Store64(new_ctrl + new_capacity + 1, copied_bytes); + + // Set The remaining bytes at the end past the cloned bytes to empty. The + // incorrectly set bytes are [new_capacity + old_capacity + 2, + // min(new_capacity + 1 + kHalfWidth, new_capacity + old_capacity + 2 + + // half_old_capacity)]. Taking the difference, we need to set min(kHalfWidth - + // (old_capacity + 1), half_old_capacity)]. Since old_capacity < kHalfWidth, + // half_old_capacity < kQuarterWidth, so we set kQuarterWidth beginning at + // new_capacity + old_capacity + 2 to kEmpty. + // Examples: + // new_ctrl = 12E0EEEEEEEEEEEE12E012EEEEEEEEE + // *new_ctrl = 12E0EEEEEEEEEEEE12E0EEEEEEEEEEE + // position S / + // + // new_ctrl = 456E0123EEEEEEEE456E0123EEEEEEE + // *new_ctrl = 456E0123EEEEEEEE456E0123EEEEEEE (no change) + // position S / + std::memset(new_ctrl + new_capacity + old_capacity_ + 2, + static_cast(ctrl_t::kEmpty), kQuarterWidth); - // Finally set sentinel to its place. + // Finally, we set the new sentinel byte. new_ctrl[new_capacity] = ctrl_t::kSentinel; } @@ -424,6 +515,129 @@ void HashSetResizeHelper::TransferSlotAfterSoo(CommonFields& c, PoisonSingleGroupEmptySlots(c, slot_size); } +namespace { + +// Called whenever the table needs to vacate empty slots either by removing +// tombstones via rehash or growth. +ABSL_ATTRIBUTE_NOINLINE +FindInfo FindInsertPositionWithGrowthOrRehash(CommonFields& common, size_t hash, + const PolicyFunctions& policy) { + const size_t cap = common.capacity(); + if (cap > Group::kWidth && + // Do these calculations in 64-bit to avoid overflow. + common.size() * uint64_t{32} <= cap * uint64_t{25}) { + // Squash DELETED without growing if there is enough capacity. + // + // Rehash in place if the current size is <= 25/32 of capacity. + // Rationale for such a high factor: 1) DropDeletesWithoutResize() is + // faster than resize, and 2) it takes quite a bit of work to add + // tombstones. In the worst case, seems to take approximately 4 + // insert/erase pairs to create a single tombstone and so if we are + // rehashing because of tombstones, we can afford to rehash-in-place as + // long as we are reclaiming at least 1/8 the capacity without doing more + // than 2X the work. (Where "work" is defined to be size() for rehashing + // or rehashing in place, and 1 for an insert or erase.) But rehashing in + // place is faster per operation than inserting or even doubling the size + // of the table, so we actually afford to reclaim even less space from a + // resize-in-place. The decision is to rehash in place if we can reclaim + // at about 1/8th of the usable capacity (specifically 3/28 of the + // capacity) which means that the total cost of rehashing will be a small + // fraction of the total work. + // + // Here is output of an experiment using the BM_CacheInSteadyState + // benchmark running the old case (where we rehash-in-place only if we can + // reclaim at least 7/16*capacity) vs. this code (which rehashes in place + // if we can recover 3/32*capacity). + // + // Note that although in the worst-case number of rehashes jumped up from + // 15 to 190, but the number of operations per second is almost the same. + // + // Abridged output of running BM_CacheInSteadyState benchmark from + // raw_hash_set_benchmark. N is the number of insert/erase operations. + // + // | OLD (recover >= 7/16 | NEW (recover >= 3/32) + // size | N/s LoadFactor NRehashes | N/s LoadFactor NRehashes + // 448 | 145284 0.44 18 | 140118 0.44 19 + // 493 | 152546 0.24 11 | 151417 0.48 28 + // 538 | 151439 0.26 11 | 151152 0.53 38 + // 583 | 151765 0.28 11 | 150572 0.57 50 + // 628 | 150241 0.31 11 | 150853 0.61 66 + // 672 | 149602 0.33 12 | 150110 0.66 90 + // 717 | 149998 0.35 12 | 149531 0.70 129 + // 762 | 149836 0.37 13 | 148559 0.74 190 + // 807 | 149736 0.39 14 | 151107 0.39 14 + // 852 | 150204 0.42 15 | 151019 0.42 15 + DropDeletesWithoutResize(common, policy); + } else { + // Otherwise grow the container. + policy.resize(common, NextCapacity(cap), HashtablezInfoHandle{}); + } + // This function is typically called with tables containing deleted slots. + // The table will be big and `FindFirstNonFullAfterResize` will always + // fallback to `find_first_non_full`. So using `find_first_non_full` directly. + return find_first_non_full(common, hash); +} + +} // namespace + +const void* GetHashRefForEmptyHasher(const CommonFields& common) { + // Empty base optimization typically make the empty base class address to be + // the same as the first address of the derived class object. + // But we generally assume that for empty hasher we can return any valid + // pointer. + return &common; +} + +size_t PrepareInsertNonSoo(CommonFields& common, size_t hash, FindInfo target, + const PolicyFunctions& policy) { + // When there are no deleted slots in the table + // and growth_left is positive, we can insert at the first + // empty slot in the probe sequence (target). + const bool use_target_hint = + // Optimization is disabled when generations are enabled. + // We have to rehash even sparse tables randomly in such mode. + !SwisstableGenerationsEnabled() && + common.growth_info().HasNoDeletedAndGrowthLeft(); + if (ABSL_PREDICT_FALSE(!use_target_hint)) { + // Notes about optimized mode when generations are disabled: + // We do not enter this branch if table has no deleted slots + // and growth_left is positive. + // We enter this branch in the following cases listed in decreasing + // frequency: + // 1. Table without deleted slots (>95% cases) that needs to be resized. + // 2. Table with deleted slots that has space for the inserting element. + // 3. Table with deleted slots that needs to be rehashed or resized. + if (ABSL_PREDICT_TRUE(common.growth_info().HasNoGrowthLeftAndNoDeleted())) { + const size_t old_capacity = common.capacity(); + policy.resize(common, NextCapacity(old_capacity), HashtablezInfoHandle{}); + target = HashSetResizeHelper::FindFirstNonFullAfterResize( + common, old_capacity, hash); + } else { + // Note: the table may have no deleted slots here when generations + // are enabled. + const bool rehash_for_bug_detection = + common.should_rehash_for_bug_detection_on_insert(); + if (rehash_for_bug_detection) { + // Move to a different heap allocation in order to detect bugs. + const size_t cap = common.capacity(); + policy.resize(common, + common.growth_left() > 0 ? cap : NextCapacity(cap), + HashtablezInfoHandle{}); + } + if (ABSL_PREDICT_TRUE(common.growth_left() > 0)) { + target = find_first_non_full(common, hash); + } else { + target = FindInsertPositionWithGrowthOrRehash(common, hash, policy); + } + } + } + PrepareInsertCommon(common); + common.growth_info().OverwriteControlAsFull(common.control()[target.offset]); + SetCtrl(common, target.offset, H2(hash), policy.slot_size); + common.infoz().RecordInsert(hash, target.probe_length); + return target.offset; +} + } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h index 1d2e2d14..1f677a4e 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h @@ -623,7 +623,12 @@ inline h2_t H2(size_t hash) { return hash & 0x7F; } // Helpers for checking the state of a control byte. inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; } -inline bool IsFull(ctrl_t c) { return c >= static_cast(0); } +inline bool IsFull(ctrl_t c) { + // Cast `c` to the underlying type instead of casting `0` to `ctrl_t` as `0` + // is not a value in the enum. Both ways are equivalent, but this way makes + // linters happier. + return static_cast>(c) >= 0; +} inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } @@ -1126,6 +1131,13 @@ class GrowthInfo { return static_cast>(growth_left_info_) > 0; } + // Returns true if the table satisfies two properties: + // 1. Guaranteed to have no kDeleted slots. + // 2. There is no growth left. + bool HasNoGrowthLeftAndNoDeleted() const { + return growth_left_info_ == 0; + } + // Returns true if table guaranteed to have no k bool HasNoDeleted() const { return static_cast>(growth_left_info_) >= 0; @@ -1374,6 +1386,8 @@ class CommonFields : public CommonFieldsGenerationInfo { // This is stored in the heap allocation before the control bytes. // TODO(b/289225379): experiment with moving growth_info back inline to // increase room for SOO. + size_t growth_left() const { return growth_info().GetGrowthLeft(); } + GrowthInfo& growth_info() { auto* gl_ptr = reinterpret_cast(control()) - 1; assert(reinterpret_cast(gl_ptr) % alignof(GrowthInfo) == 0); @@ -1933,8 +1947,7 @@ class HashSetResizeHelper { // will be no performance benefit. // It has implicit assumption that `resize` will call // `GrowSizeIntoSingleGroup*` in case `IsGrowingIntoSingleGroupApplicable`. - // Falls back to `find_first_non_full` in case of big groups, so it is - // safe to use after `rehash_and_grow_if_necessary`. + // Falls back to `find_first_non_full` in case of big groups. static FindInfo FindFirstNonFullAfterResize(const CommonFields& c, size_t old_capacity, size_t hash) { @@ -2216,14 +2229,22 @@ size_t PrepareInsertAfterSoo(size_t hash, size_t slot_size, struct PolicyFunctions { size_t slot_size; + // Returns the pointer to the hash function stored in the set. + const void* (*hash_fn)(const CommonFields& common); + // Returns the hash of the pointed-to slot. size_t (*hash_slot)(const void* hash_fn, void* slot); - // Transfer the contents of src_slot to dst_slot. + // Transfers the contents of src_slot to dst_slot. void (*transfer)(void* set, void* dst_slot, void* src_slot); - // Deallocate the backing store from common. + // Deallocates the backing store from common. void (*dealloc)(CommonFields& common, const PolicyFunctions& policy); + + // Resizes set to the new capacity. + // Arguments are used as in raw_hash_set::resize_impl. + void (*resize)(CommonFields& common, size_t new_capacity, + HashtablezInfoHandle forced_infoz); }; // ClearBackingArray clears the backing array, either modifying it in place, @@ -2261,9 +2282,26 @@ ABSL_ATTRIBUTE_NOINLINE void TransferRelocatable(void*, void* dst, void* src) { memcpy(dst, src, SizeOfSlot); } -// Type-erased version of raw_hash_set::drop_deletes_without_resize. -void DropDeletesWithoutResize(CommonFields& common, const void* hash_fn, - const PolicyFunctions& policy, void* tmp_space); +// Type erased raw_hash_set::get_hash_ref_fn for the empty hash function case. +const void* GetHashRefForEmptyHasher(const CommonFields& common); + +// Given the hash of a value not currently in the table and the first empty +// slot in the probe sequence, finds a viable slot index to insert it at. +// +// In case there's no space left, the table can be resized or rehashed +// (for tables with deleted slots, see FindInsertPositionWithGrowthOrRehash). +// +// In the case of absence of deleted slots and positive growth_left, the element +// can be inserted in the provided `target` position. +// +// When the table has deleted slots (according to GrowthInfo), the target +// position will be searched one more time using `find_first_non_full`. +// +// REQUIRES: Table is not SOO. +// REQUIRES: At least one non-full slot available. +// REQUIRES: `target` is a valid empty position to insert. +size_t PrepareInsertNonSoo(CommonFields& common, size_t hash, FindInfo target, + const PolicyFunctions& policy); // A SwissTable. // @@ -3533,7 +3571,7 @@ class raw_hash_set { // common(), old_capacity, hash) // can be called right after `resize`. void resize(size_t new_capacity) { - resize_impl(new_capacity, HashtablezInfoHandle{}); + raw_hash_set::resize_impl(common(), new_capacity, HashtablezInfoHandle{}); } // As above, except that we also accept a pre-sampled, forced infoz for @@ -3541,19 +3579,24 @@ class raw_hash_set { // store the infoz. void resize_with_soo_infoz(HashtablezInfoHandle forced_infoz) { assert(forced_infoz.IsSampled()); - resize_impl(NextCapacity(SooCapacity()), forced_infoz); + raw_hash_set::resize_impl(common(), NextCapacity(SooCapacity()), + forced_infoz); } - ABSL_ATTRIBUTE_NOINLINE void resize_impl( - size_t new_capacity, HashtablezInfoHandle forced_infoz) { + // Resizes set to the new capacity. + // It is a static function in order to use its pointer in GetPolicyFunctions. + ABSL_ATTRIBUTE_NOINLINE static void resize_impl( + CommonFields& common, size_t new_capacity, + HashtablezInfoHandle forced_infoz) { + raw_hash_set* set = reinterpret_cast(&common); assert(IsValidCapacity(new_capacity)); - assert(!fits_in_soo(new_capacity)); - const bool was_soo = is_soo(); - const bool had_soo_slot = was_soo && !empty(); + assert(!set->fits_in_soo(new_capacity)); + const bool was_soo = set->is_soo(); + const bool had_soo_slot = was_soo && !set->empty(); const ctrl_t soo_slot_h2 = - had_soo_slot ? static_cast(H2(hash_of(soo_slot()))) + had_soo_slot ? static_cast(H2(set->hash_of(set->soo_slot()))) : ctrl_t::kEmpty; - HashSetResizeHelper resize_helper(common(), was_soo, had_soo_slot, + HashSetResizeHelper resize_helper(common, was_soo, had_soo_slot, forced_infoz); // Initialize HashSetResizeHelper::old_heap_or_soo_. We can't do this in // HashSetResizeHelper constructor because it can't transfer slots when @@ -3561,11 +3604,12 @@ class raw_hash_set { // TODO(b/289225379): try to handle more of the SOO cases inside // InitializeSlots. See comment on cl/555990034 snapshot #63. if (PolicyTraits::transfer_uses_memcpy() || !had_soo_slot) { - resize_helper.old_heap_or_soo() = common().heap_or_soo(); + resize_helper.old_heap_or_soo() = common.heap_or_soo(); } else { - transfer(to_slot(resize_helper.old_soo_data()), soo_slot()); + set->transfer(set->to_slot(resize_helper.old_soo_data()), + set->soo_slot()); } - common().set_capacity(new_capacity); + common.set_capacity(new_capacity); // Note that `InitializeSlots` does different number initialization steps // depending on the values of `transfer_uses_memcpy` and capacities. // Refer to the comment in `InitializeSlots` for more details. @@ -3573,7 +3617,7 @@ class raw_hash_set { resize_helper.InitializeSlots( - common(), CharAlloc(alloc_ref()), soo_slot_h2, sizeof(key_type), + common, CharAlloc(set->alloc_ref()), soo_slot_h2, sizeof(key_type), sizeof(value_type)); // In the SooEnabled() case, capacity is never 0 so we don't check. @@ -3585,30 +3629,30 @@ class raw_hash_set { // Nothing more to do in this case. if (was_soo && !had_soo_slot) return; - slot_type* new_slots = slot_array(); + slot_type* new_slots = set->slot_array(); if (grow_single_group) { if (PolicyTraits::transfer_uses_memcpy()) { // InitializeSlots did all the work. return; } if (was_soo) { - transfer(new_slots + resize_helper.SooSlotIndex(), - to_slot(resize_helper.old_soo_data())); + set->transfer(new_slots + resize_helper.SooSlotIndex(), + to_slot(resize_helper.old_soo_data())); return; } else { // We want GrowSizeIntoSingleGroup to be called here in order to make // InitializeSlots not depend on PolicyTraits. - resize_helper.GrowSizeIntoSingleGroup(common(), - alloc_ref()); + resize_helper.GrowSizeIntoSingleGroup(common, + set->alloc_ref()); } } else { // InitializeSlots prepares control bytes to correspond to empty table. const auto insert_slot = [&](slot_type* slot) { - size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, + size_t hash = PolicyTraits::apply(HashElement{set->hash_ref()}, PolicyTraits::element(slot)); - auto target = find_first_non_full(common(), hash); - SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type)); - transfer(new_slots + target.offset, slot); + auto target = find_first_non_full(common, hash); + SetCtrl(common, target.offset, H2(hash), sizeof(slot_type)); + set->transfer(new_slots + target.offset, slot); return target.probe_length; }; if (was_soo) { @@ -3623,80 +3667,13 @@ class raw_hash_set { total_probe_length += insert_slot(old_slots + i); } } - infoz().RecordRehash(total_probe_length); + common.infoz().RecordRehash(total_probe_length); } } - resize_helper.DeallocateOld(CharAlloc(alloc_ref()), + resize_helper.DeallocateOld(CharAlloc(set->alloc_ref()), sizeof(slot_type)); } - // Prunes control bytes to remove as many tombstones as possible. - // - // See the comment on `rehash_and_grow_if_necessary()`. - inline void drop_deletes_without_resize() { - // Stack-allocate space for swapping elements. - alignas(slot_type) unsigned char tmp[sizeof(slot_type)]; - DropDeletesWithoutResize(common(), &hash_ref(), GetPolicyFunctions(), tmp); - } - - // Called whenever the table *might* need to conditionally grow. - // - // This function is an optimization opportunity to perform a rehash even when - // growth is unnecessary, because vacating tombstones is beneficial for - // performance in the long-run. - void rehash_and_grow_if_necessary() { - const size_t cap = capacity(); - if (cap > Group::kWidth && - // Do these calculations in 64-bit to avoid overflow. - size() * uint64_t{32} <= cap * uint64_t{25}) { - // Squash DELETED without growing if there is enough capacity. - // - // Rehash in place if the current size is <= 25/32 of capacity. - // Rationale for such a high factor: 1) drop_deletes_without_resize() is - // faster than resize, and 2) it takes quite a bit of work to add - // tombstones. In the worst case, seems to take approximately 4 - // insert/erase pairs to create a single tombstone and so if we are - // rehashing because of tombstones, we can afford to rehash-in-place as - // long as we are reclaiming at least 1/8 the capacity without doing more - // than 2X the work. (Where "work" is defined to be size() for rehashing - // or rehashing in place, and 1 for an insert or erase.) But rehashing in - // place is faster per operation than inserting or even doubling the size - // of the table, so we actually afford to reclaim even less space from a - // resize-in-place. The decision is to rehash in place if we can reclaim - // at about 1/8th of the usable capacity (specifically 3/28 of the - // capacity) which means that the total cost of rehashing will be a small - // fraction of the total work. - // - // Here is output of an experiment using the BM_CacheInSteadyState - // benchmark running the old case (where we rehash-in-place only if we can - // reclaim at least 7/16*capacity) vs. this code (which rehashes in place - // if we can recover 3/32*capacity). - // - // Note that although in the worst-case number of rehashes jumped up from - // 15 to 190, but the number of operations per second is almost the same. - // - // Abridged output of running BM_CacheInSteadyState benchmark from - // raw_hash_set_benchmark. N is the number of insert/erase operations. - // - // | OLD (recover >= 7/16 | NEW (recover >= 3/32) - // size | N/s LoadFactor NRehashes | N/s LoadFactor NRehashes - // 448 | 145284 0.44 18 | 140118 0.44 19 - // 493 | 152546 0.24 11 | 151417 0.48 28 - // 538 | 151439 0.26 11 | 151152 0.53 38 - // 583 | 151765 0.28 11 | 150572 0.57 50 - // 628 | 150241 0.31 11 | 150853 0.61 66 - // 672 | 149602 0.33 12 | 150110 0.66 90 - // 717 | 149998 0.35 12 | 149531 0.70 129 - // 762 | 149836 0.37 13 | 148559 0.74 190 - // 807 | 149736 0.39 14 | 151107 0.39 14 - // 852 | 150204 0.42 15 | 151019 0.42 15 - drop_deletes_without_resize(); - } else { - // Otherwise grow the container. - resize(NextCapacity(cap)); - } - } - // Casting directly from e.g. char* to slot_type* can cause compilation errors // on objective-C. This function converts to void* first, avoiding the issue. static slot_type* to_slot(void* buf) { @@ -3817,6 +3794,7 @@ class raw_hash_set { template std::pair find_or_prepare_insert_non_soo(const K& key) { + assert(!is_soo()); prefetch_heap_block(); auto hash = hash_ref()(key); auto seq = probe(common(), hash); @@ -3833,9 +3811,10 @@ class raw_hash_set { if (ABSL_PREDICT_TRUE(mask_empty)) { size_t target = seq.offset( GetInsertionOffset(mask_empty, capacity(), hash, control())); - return { - iterator_at(prepare_insert(hash, FindInfo{target, seq.index()})), - true}; + return {iterator_at(PrepareInsertNonSoo(common(), hash, + FindInfo{target, seq.index()}, + GetPolicyFunctions())), + true}; } seq.next(); assert(seq.index() <= capacity() && "full table!"); @@ -3852,63 +3831,6 @@ class raw_hash_set { return find_or_prepare_insert_non_soo(key); } - // Given the hash of a value not currently in the table and the first empty - // slot in the probe sequence, finds the viable slot index to insert it at. - // - // In case there's no space left, the table can be resized or rehashed - // (see rehash_and_grow_if_necessary). - // - // In case of absence of deleted slots and positive growth_left, element can - // be inserted in the provided `target` position. - // - // When table has deleted slots (according to GrowthInfo), target position - // will be searched one more time using `find_first_non_full`. - // - // REQUIRES: At least one non-full slot available. - // REQUIRES: `target` is a valid empty position to insert. - size_t prepare_insert(size_t hash, FindInfo target) ABSL_ATTRIBUTE_NOINLINE { - assert(!is_soo()); - // When there are no deleted slots in the table - // and growth_left is positive, we can insert at the first - // empty slot in the probe sequence (target). - bool use_target_hint = false; - // Optimization is disabled on enabled generations. - // We have to rehash even sparse tables randomly in such mode. -#ifndef ABSL_SWISSTABLE_ENABLE_GENERATIONS - use_target_hint = growth_info().HasNoDeletedAndGrowthLeft(); -#endif - if (ABSL_PREDICT_FALSE(!use_target_hint)) { - const bool rehash_for_bug_detection = - common().should_rehash_for_bug_detection_on_insert(); - if (rehash_for_bug_detection) { - // Move to a different heap allocation in order to detect bugs. - const size_t cap = capacity(); - resize(growth_left() > 0 ? cap : NextCapacity(cap)); - } - if (!rehash_for_bug_detection && - ABSL_PREDICT_FALSE(growth_left() == 0)) { - const size_t old_capacity = capacity(); - rehash_and_grow_if_necessary(); - // NOTE: It is safe to use `FindFirstNonFullAfterResize` after - // `rehash_and_grow_if_necessary`, whether capacity changes or not. - // `rehash_and_grow_if_necessary` may *not* call `resize` - // and perform `drop_deletes_without_resize` instead. But this - // could happen only on big tables and will not change capacity. - // For big tables `FindFirstNonFullAfterResize` will always - // fallback to normal `find_first_non_full`. - target = HashSetResizeHelper::FindFirstNonFullAfterResize( - common(), old_capacity, hash); - } else { - target = find_first_non_full(common(), hash); - } - } - PrepareInsertCommon(common()); - growth_info().OverwriteControlAsFull(control()[target.offset]); - SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type)); - infoz().RecordInsert(hash, target.probe_length); - return target.offset; - } - // Constructs the value in the space pointed by the iterator. This only works // after an unsuccessful find_or_prepare_insert() and before any other // modifications happen in the raw_hash_set. @@ -3949,7 +3871,7 @@ class raw_hash_set { // See `CapacityToGrowth()`. size_t growth_left() const { assert(!is_soo()); - return growth_info().GetGrowthLeft(); + return common().growth_left(); } GrowthInfo& growth_info() { @@ -4009,6 +3931,10 @@ class raw_hash_set { return settings_.template get<3>(); } + static const void* get_hash_ref_fn(const CommonFields& common) { + auto* h = reinterpret_cast(&common); + return &h->hash_ref(); + } static void transfer_slot_fn(void* set, void* dst, void* src) { auto* h = static_cast(set); h->transfer(static_cast(dst), static_cast(src)); @@ -4030,6 +3956,10 @@ class raw_hash_set { static const PolicyFunctions& GetPolicyFunctions() { static constexpr PolicyFunctions value = { sizeof(slot_type), + // TODO(b/328722020): try to type erase + // for standard layout and alignof(Hash) <= alignof(CommonFields). + std::is_empty::value ? &GetHashRefForEmptyHasher + : &raw_hash_set::get_hash_ref_fn, PolicyTraits::template get_hash_slot_fn(), PolicyTraits::transfer_uses_memcpy() ? TransferRelocatable @@ -4037,6 +3967,7 @@ class raw_hash_set { (std::is_same>::value ? &DeallocateStandard : &raw_hash_set::dealloc_fn), + &raw_hash_set::resize_impl, }; return value; } diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc index c4e05d60..10f793ef 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc @@ -126,6 +126,22 @@ TEST(GrowthInfoTest, HasNoDeletedAndGrowthLeft) { EXPECT_TRUE(gi.HasNoDeletedAndGrowthLeft()); } +TEST(GrowthInfoTest, HasNoGrowthLeftAndNoDeleted) { + GrowthInfo gi; + gi.InitGrowthLeftNoDeleted(1); + EXPECT_FALSE(gi.HasNoGrowthLeftAndNoDeleted()); + gi.OverwriteEmptyAsFull(); + EXPECT_TRUE(gi.HasNoGrowthLeftAndNoDeleted()); + gi.OverwriteFullAsDeleted(); + EXPECT_FALSE(gi.HasNoGrowthLeftAndNoDeleted()); + gi.OverwriteFullAsEmpty(); + EXPECT_FALSE(gi.HasNoGrowthLeftAndNoDeleted()); + gi.InitGrowthLeftNoDeleted(0); + EXPECT_TRUE(gi.HasNoGrowthLeftAndNoDeleted()); + gi.OverwriteFullAsEmpty(); + EXPECT_FALSE(gi.HasNoGrowthLeftAndNoDeleted()); +} + TEST(GrowthInfoTest, OverwriteFullAsEmpty) { GrowthInfo gi; gi.InitGrowthLeftNoDeleted(5); diff --git a/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake b/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake index 3f737c81..1afb9610 100644 --- a/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake +++ b/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake @@ -3,7 +3,7 @@ include(GENERATED_AbseilCopts) set(ABSL_DEFAULT_LINKOPTS "") -if (BUILD_SHARED_LIBS AND MSVC) +if (BUILD_SHARED_LIBS AND (MSVC OR ABSL_BUILD_MONOLITHIC_SHARED_LIBS)) set(ABSL_BUILD_DLL TRUE) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) else() diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake index d96bd839..da2282fe 100644 --- a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake +++ b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake @@ -87,7 +87,6 @@ list(APPEND ABSL_LLVM_FLAGS "-Wc++98-compat-extra-semi" "-Wcast-qual" "-Wconversion" - "-Wdead-code-aggressive" "-Wdeprecated-pragma" "-Wfloat-overflow-conversion" "-Wfloat-zero-conversion" @@ -128,7 +127,6 @@ list(APPEND ABSL_LLVM_TEST_FLAGS "-Wc++98-compat-extra-semi" "-Wcast-qual" "-Wconversion" - "-Wdead-code-aggressive" "-Wdeprecated-pragma" "-Wfloat-overflow-conversion" "-Wfloat-zero-conversion" diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl index 0a344236..b9e0071e 100644 --- a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl +++ b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl @@ -88,7 +88,6 @@ ABSL_LLVM_FLAGS = [ "-Wc++98-compat-extra-semi", "-Wcast-qual", "-Wconversion", - "-Wdead-code-aggressive", "-Wdeprecated-pragma", "-Wfloat-overflow-conversion", "-Wfloat-zero-conversion", @@ -129,7 +128,6 @@ ABSL_LLVM_TEST_FLAGS = [ "-Wc++98-compat-extra-semi", "-Wcast-qual", "-Wconversion", - "-Wdead-code-aggressive", "-Wdeprecated-pragma", "-Wfloat-overflow-conversion", "-Wfloat-zero-conversion", diff --git a/third_party/abseil-cpp/absl/copts/copts.py b/third_party/abseil-cpp/absl/copts/copts.py index 1aa09249..2d85ac74 100644 --- a/third_party/abseil-cpp/absl/copts/copts.py +++ b/third_party/abseil-cpp/absl/copts/copts.py @@ -47,7 +47,6 @@ "-Wc++98-compat-extra-semi", "-Wcast-qual", "-Wconversion", - "-Wdead-code-aggressive", "-Wdeprecated-pragma", "-Wfloat-overflow-conversion", "-Wfloat-zero-conversion", diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle.cc index 71d4eb0a..8bff73bb 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle.cc @@ -81,6 +81,7 @@ static const AbbrevPair kOperatorList[] = { {"rs", ">>", 2}, {"lS", "<<=", 2}, {"rS", ">>=", 2}, + {"ss", "<=>", 2}, {"eq", "==", 2}, {"ne", "!=", 2}, {"lt", "<", 2}, @@ -100,6 +101,7 @@ static const AbbrevPair kOperatorList[] = { {"qu", "?", 3}, {"st", "sizeof", 0}, // Special syntax {"sz", "sizeof", 1}, // Not a real operator name, but used in expressions. + {"sZ", "sizeof...", 0}, // Special syntax {nullptr, nullptr, 0}, }; @@ -298,8 +300,8 @@ static bool ParseOneCharToken(State *state, const char one_char_token) { return false; } -// Returns true and advances "mangled_cur" if we find "two_char_token" -// at "mangled_cur" position. It is assumed that "two_char_token" does +// Returns true and advances "mangled_idx" if we find "two_char_token" +// at "mangled_idx" position. It is assumed that "two_char_token" does // not contain '\0'. static bool ParseTwoCharToken(State *state, const char *two_char_token) { ComplexityGuard guard(state); @@ -312,6 +314,37 @@ static bool ParseTwoCharToken(State *state, const char *two_char_token) { return false; } +// Returns true and advances "mangled_idx" if we find "three_char_token" +// at "mangled_idx" position. It is assumed that "three_char_token" does +// not contain '\0'. +static bool ParseThreeCharToken(State *state, const char *three_char_token) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == three_char_token[0] && + RemainingInput(state)[1] == three_char_token[1] && + RemainingInput(state)[2] == three_char_token[2]) { + state->parse_state.mangled_idx += 3; + return true; + } + return false; +} + +// Returns true and advances "mangled_idx" if we find a copy of the +// NUL-terminated string "long_token" at "mangled_idx" position. +static bool ParseLongToken(State *state, const char *long_token) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + int i = 0; + for (; long_token[i] != '\0'; ++i) { + // Note that we cannot run off the end of the NUL-terminated input here. + // Inside the loop body, long_token[i] is known to be different from NUL. + // So if we read the NUL on the end of the input here, we return at once. + if (RemainingInput(state)[i] != long_token[i]) return false; + } + state->parse_state.mangled_idx += i; + return true; +} + // Returns true and advances "mangled_cur" if we find any character in // "char_class" at "mangled_cur" position. static bool ParseCharClass(State *state, const char *char_class) { @@ -568,6 +601,7 @@ static bool ParseCVQualifiers(State *state); static bool ParseBuiltinType(State *state); static bool ParseFunctionType(State *state); static bool ParseBareFunctionType(State *state); +static bool ParseOverloadAttribute(State *state); static bool ParseClassEnumType(State *state); static bool ParseArrayType(State *state); static bool ParsePointerToMemberType(State *state); @@ -578,10 +612,16 @@ static bool ParseTemplateArgs(State *state); static bool ParseTemplateArg(State *state); static bool ParseBaseUnresolvedName(State *state); static bool ParseUnresolvedName(State *state); +static bool ParseUnresolvedQualifierLevel(State *state); +static bool ParseUnionSelector(State* state); +static bool ParseFunctionParam(State* state); +static bool ParseBracedExpression(State *state); static bool ParseExpression(State *state); static bool ParseExprPrimary(State *state); -static bool ParseExprCastValue(State *state); +static bool ParseExprCastValueAndTrailingE(State *state); static bool ParseQRequiresClauseExpr(State *state); +static bool ParseRequirement(State *state); +static bool ParseTypeConstraint(State *state); static bool ParseLocalName(State *state); static bool ParseLocalNameSuffix(State *state); static bool ParseDiscriminator(State *state); @@ -739,6 +779,7 @@ static bool ParseNestedName(State *state) { // ::= // ::= // ::= +// ::= // ::= // ::= # empty // ::= <(template) unqualified-name> @@ -750,7 +791,7 @@ static bool ParsePrefix(State *state) { bool has_something = false; while (true) { MaybeAppendSeparator(state); - if (ParseTemplateParam(state) || + if (ParseTemplateParam(state) || ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/true) || ParseUnscopedName(state) || (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) { @@ -840,7 +881,11 @@ static bool ParseLocalSourceName(State *state) { // ::= Ut [<(nonnegative) number>] _ // ::= // ::= Ul E [<(nonnegative) number>] _ -// ::= <(parameter) type>+ +// ::= * <(parameter) type>+ +// +// For * in see: +// +// https://github.com/itanium-cxx-abi/cxx-abi/issues/31 static bool ParseUnnamedTypeName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; @@ -863,6 +908,7 @@ static bool ParseUnnamedTypeName(State *state) { // Closure type. which = -1; if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) && + ZeroOrMore(ParseTemplateParamDecl, state) && OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) && ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) && which <= std::numeric_limits::max() - 2 && // Don't overflow. @@ -1023,7 +1069,8 @@ static bool ParseOperatorName(State *state, int *arity) { // ::= TT // ::= TI // ::= TS -// ::= TH # thread-local +// ::= TW # thread-local wrapper +// ::= TH # thread-local initialization // ::= Tc <(base) encoding> // ::= GV <(object) name> // ::= T <(base) encoding> @@ -1036,13 +1083,31 @@ static bool ParseOperatorName(State *state, int *arity) { // ::= Th <(base) encoding> // ::= Tv <(base) encoding> // -// Note: we don't care much about them since they don't appear in -// stack traces. The are special data. +// Note: Most of these are special data, not functions that occur in stack +// traces. Exceptions are TW and TH, which denote functions supporting the +// thread_local feature. For these see: +// +// https://maskray.me/blog/2021-02-14-all-about-thread-local-storage static bool ParseSpecialName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; - if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && + + if (ParseTwoCharToken(state, "TW")) { + MaybeAppend(state, "thread-local wrapper routine for "); + if (ParseName(state)) return true; + state->parse_state = copy; + return false; + } + + if (ParseTwoCharToken(state, "TH")) { + MaybeAppend(state, "thread-local initialization routine for "); + if (ParseName(state)) return true; + state->parse_state = copy; + return false; + } + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTIS") && ParseType(state)) { return true; } @@ -1210,6 +1275,7 @@ static bool ParseDecltype(State *state) { // ::= // ::= Dp # pack expansion of (C++0x) // ::= Dv _ # GNU vector extension +// ::= Dk # constrained auto // static bool ParseType(State *state) { ComplexityGuard guard(state); @@ -1282,7 +1348,15 @@ static bool ParseType(State *state) { } state->parse_state = copy; - return false; + if (ParseTwoCharToken(state, "Dk") && ParseTypeConstraint(state)) { + return true; + } + state->parse_state = copy; + + // For this notation see CXXNameMangler::mangleType in Clang's source code. + // The relevant logic and its comment "not clear how to mangle this!" date + // from 2011, so it may be with us awhile. + return ParseLongToken(state, "_SUBSTPACK_"); } // ::= [r] [V] [K] @@ -1365,28 +1439,43 @@ static bool ParseExceptionSpec(State *state) { return false; } -// ::= [exception-spec] F [Y] [O] E +// ::= +// [exception-spec] F [Y] [] E +// +// ::= R | O static bool ParseFunctionType(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; - if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && - Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && - Optional(ParseOneCharToken(state, 'O')) && - ParseOneCharToken(state, 'E')) { - return true; + Optional(ParseExceptionSpec(state)); + if (!ParseOneCharToken(state, 'F')) { + state->parse_state = copy; + return false; } - state->parse_state = copy; - return false; + Optional(ParseOneCharToken(state, 'Y')); + if (!ParseBareFunctionType(state)) { + state->parse_state = copy; + return false; + } + Optional(ParseCharClass(state, "RO")); + if (!ParseOneCharToken(state, 'E')) { + state->parse_state = copy; + return false; + } + return true; } -// ::= <(signature) type>+ +// ::= * <(signature) type>+ +// +// The * prefix is nonstandard; see the comment on +// ParseOverloadAttribute. static bool ParseBareFunctionType(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; DisableAppend(state); - if (OneOrMore(ParseType, state)) { + if (ZeroOrMore(ParseOverloadAttribute, state) && + OneOrMore(ParseType, state)) { RestoreAppend(state, copy.append); MaybeAppend(state, "()"); return true; @@ -1395,6 +1484,25 @@ static bool ParseBareFunctionType(State *state) { return false; } +// ::= Ua +// +// The nonstandard production is sufficient to accept the +// current implementation of __attribute__((enable_if(condition, "message"))) +// and future attributes of a similar shape. See +// https://clang.llvm.org/docs/AttributeReference.html#enable-if and the +// definition of CXXNameMangler::mangleFunctionEncodingBareType in Clang's +// source code. +static bool ParseOverloadAttribute(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "Ua") && ParseName(state)) { + return true; + } + state->parse_state = copy; + return false; +} + // ::= static bool ParseClassEnumType(State *state) { ComplexityGuard guard(state); @@ -1628,7 +1736,7 @@ static bool ParseTemplateArg(State *state) { // ::= L [] [ E] if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) { copy = state->parse_state; - if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) { + if (ParseExprCastValueAndTrailingE(state)) { return true; } state->parse_state = copy; @@ -1726,7 +1834,7 @@ static bool ParseUnresolvedName(State *state) { if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') && ParseUnresolvedType(state) && - OneOrMore(/* ::= */ ParseSimpleId, state) && + OneOrMore(ParseUnresolvedQualifierLevel, state) && ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { return true; } @@ -1734,7 +1842,7 @@ static bool ParseUnresolvedName(State *state) { if (Optional(ParseTwoCharToken(state, "gs")) && ParseTwoCharToken(state, "sr") && - OneOrMore(/* ::= */ ParseSimpleId, state) && + OneOrMore(ParseUnresolvedQualifierLevel, state) && ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { return true; } @@ -1743,26 +1851,128 @@ static bool ParseUnresolvedName(State *state) { return false; } +// ::= +// ::= +// +// The production is nonstandard but is observed +// in practice. An upstream discussion on the best shape of +// has not converged: +// +// https://github.com/itanium-cxx-abi/cxx-abi/issues/38 +static bool ParseUnresolvedQualifierLevel(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseSimpleId(state)) return true; + + ParseState copy = state->parse_state; + if (ParseSubstitution(state, /*accept_std=*/false) && + ParseTemplateArgs(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// ::= _ [] +// +// https://github.com/itanium-cxx-abi/cxx-abi/issues/47 +static bool ParseUnionSelector(State *state) { + return ParseOneCharToken(state, '_') && Optional(ParseNumber(state, nullptr)); +} + +// ::= fp <(top-level) CV-qualifiers> _ +// ::= fp <(top-level) CV-qualifiers> _ +// ::= fL p <(top-level) CV-qualifiers> _ +// ::= fL p <(top-level) CV-qualifiers> _ +// ::= fpT # this +static bool ParseFunctionParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + + // Function-param expression (level 0). + if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Function-param expression (level 1+). + if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && + ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + return ParseThreeCharToken(state, "fpT"); +} + +// ::= +// ::= di +// ::= dx +// ::= dX +static bool ParseBracedExpression(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + + if (ParseTwoCharToken(state, "di") && ParseSourceName(state) && + ParseBracedExpression(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "dx") && ParseExpression(state) && + ParseBracedExpression(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "dX") && + ParseExpression(state) && ParseExpression(state) && + ParseBracedExpression(state)) { + return true; + } + state->parse_state = copy; + + return ParseExpression(state); +} + // ::= <1-ary operator-name> // ::= <2-ary operator-name> // ::= <3-ary operator-name> // ::= cl + E // ::= cp * E # Clang-specific. +// ::= so [] * [p] E // ::= cv # type (expression) // ::= cv _ * E # type (expr-list) +// ::= tl * E +// ::= dc +// ::= sc +// ::= cc +// ::= rc // ::= st // ::= // ::= +// ::= sZ +// ::= sZ // ::= // ::= dt # expr.name // ::= pt # expr->name // ::= sp # argument pack expansion +// ::= fl +// ::= fr +// ::= fL +// ::= fR // ::= sr // ::= sr -// ::= fp <(top-level) CV-qualifiers> _ -// ::= fp <(top-level) CV-qualifiers> _ -// ::= fL p <(top-level) CV-qualifiers> _ -// ::= fL p <(top-level) CV-qualifiers> _ +// ::= u * E # vendor extension +// ::= rq + E +// ::= rQ _ + E static bool ParseExpression(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; @@ -1787,17 +1997,35 @@ static bool ParseExpression(State *state) { } state->parse_state = copy; - // Function-param expression (level 0). - if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && - Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + // ::= so [] * [p] E + // + // https://github.com/itanium-cxx-abi/cxx-abi/issues/47 + if (ParseTwoCharToken(state, "so") && ParseType(state) && + ParseExpression(state) && Optional(ParseNumber(state, nullptr)) && + ZeroOrMore(ParseUnionSelector, state) && + Optional(ParseOneCharToken(state, 'p')) && + ParseOneCharToken(state, 'E')) { return true; } state->parse_state = copy; - // Function-param expression (level 1+). - if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && - ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && - Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + // ::= + if (ParseFunctionParam(state)) return true; + state->parse_state = copy; + + // ::= tl * E + if (ParseTwoCharToken(state, "tl") && ParseType(state) && + ZeroOrMore(ParseBracedExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // dynamic_cast, static_cast, const_cast, reinterpret_cast. + // + // ::= (dc | sc | cc | rc) + if (ParseCharClass(state, "dscr") && ParseOneCharToken(state, 'c') && + ParseType(state) && ParseExpression(state)) { return true; } state->parse_state = copy; @@ -1845,9 +2073,42 @@ static bool ParseExpression(State *state) { } state->parse_state = copy; + // sizeof...(pack) + // + // ::= sZ + // ::= sZ + if (ParseTwoCharToken(state, "sZ") && + (ParseFunctionParam(state) || ParseTemplateParam(state))) { + return true; + } + state->parse_state = copy; + + // Unary folds (... op pack) and (pack op ...). + // + // ::= fl + // ::= fr + if ((ParseTwoCharToken(state, "fl") || ParseTwoCharToken(state, "fr")) && + ParseOperatorName(state, nullptr) && ParseExpression(state)) { + return true; + } + state->parse_state = copy; + + // Binary folds (init op ... op pack) and (pack op ... op init). + // + // ::= fL + // ::= fR + if ((ParseTwoCharToken(state, "fL") || ParseTwoCharToken(state, "fR")) && + ParseOperatorName(state, nullptr) && ParseExpression(state) && + ParseExpression(state)) { + return true; + } + state->parse_state = copy; + // Object and pointer member access expressions. + // + // ::= (dt | pt) if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) && - ParseExpression(state) && ParseType(state)) { + ParseExpression(state) && ParseUnresolvedName(state)) { return true; } state->parse_state = copy; @@ -1867,6 +2128,32 @@ static bool ParseExpression(State *state) { } state->parse_state = copy; + // Vendor extended expressions + if (ParseOneCharToken(state, 'u') && ParseSourceName(state) && + ZeroOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // ::= rq + E + // + // https://github.com/itanium-cxx-abi/cxx-abi/issues/24 + if (ParseTwoCharToken(state, "rq") && OneOrMore(ParseRequirement, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // ::= rQ _ + E + // + // https://github.com/itanium-cxx-abi/cxx-abi/issues/24 + if (ParseTwoCharToken(state, "rQ") && ParseBareFunctionType(state) && + ParseOneCharToken(state, '_') && OneOrMore(ParseRequirement, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return ParseUnresolvedName(state); } @@ -1912,10 +2199,35 @@ static bool ParseExprPrimary(State *state) { return false; } - // The merged cast production. - if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseExprCastValue(state)) { - return true; + if (ParseOneCharToken(state, 'L')) { + // There are two special cases in which a literal may or must contain a type + // without a value. The first is that both LDnE and LDn0E are valid + // encodings of nullptr, used in different situations. Recognize LDnE here, + // leaving LDn0E to be recognized by the general logic afterward. + if (ParseThreeCharToken(state, "DnE")) return true; + + // The second special case is a string literal, currently mangled in C++98 + // style as LA_KcE. This is inadequate to support C++11 and + // later versions, and the discussion of this problem has not converged. + // + // https://github.com/itanium-cxx-abi/cxx-abi/issues/64 + // + // For now the bare-type mangling is what's used in practice, so we + // recognize this form and only this form if an array type appears here. + // Someday we'll probably have to accept a new form of value mangling in + // LA...E constructs. (Note also that C++20 allows a wide range of + // class-type objects as template arguments, so someday their values will be + // mangled and we'll have to recognize them here too.) + if (RemainingInput(state)[0] == 'A' /* an array type follows */) { + if (ParseType(state) && ParseOneCharToken(state, 'E')) return true; + state->parse_state = copy; + return false; + } + + // The merged cast production. + if (ParseType(state) && ParseExprCastValueAndTrailingE(state)) { + return true; + } } state->parse_state = copy; @@ -1929,7 +2241,7 @@ static bool ParseExprPrimary(State *state) { } // or , followed by 'E', as described above ParseExprPrimary. -static bool ParseExprCastValue(State *state) { +static bool ParseExprCastValueAndTrailingE(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; // We have to be able to backtrack after accepting a number because we could @@ -1983,31 +2295,93 @@ static bool ParseQRequiresClauseExpr(State *state) { return false; } +// ::= X [N] [R ] +// ::= T +// ::= Q +// +// ::= +// +// https://github.com/itanium-cxx-abi/cxx-abi/issues/24 +static bool ParseRequirement(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + Optional(ParseOneCharToken(state, 'N')) && + // This logic backtracks cleanly if we eat an R but a valid type doesn't + // follow it. + (!ParseOneCharToken(state, 'R') || ParseTypeConstraint(state))) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseType(state)) return true; + state->parse_state = copy; + + if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) return true; + state->parse_state = copy; + + return false; +} + +// ::= +static bool ParseTypeConstraint(State *state) { + return ParseName(state); +} + // ::= Z <(function) encoding> E <(entity) name> [] // ::= Z <(function) encoding> E s [] +// ::= Z <(function) encoding> E d [<(parameter) number>] _ // // Parsing a common prefix of these two productions together avoids an // exponential blowup of backtracking. Parse like: // := Z E // ::= s [] +// ::= d [<(parameter) number>] _ // ::= [] static bool ParseLocalNameSuffix(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + // ::= d [<(parameter) number>] _ + if (ParseOneCharToken(state, 'd') && + (IsDigit(RemainingInput(state)[0]) || RemainingInput(state)[0] == '_')) { + int number = -1; + Optional(ParseNumber(state, &number)); + number += 2; + + // The ::{default arg#1}:: infix must be rendered before the lambda itself, + // so print this before parsing the rest of the . + MaybeAppend(state, "::{default arg#"); + MaybeAppendDecimal(state, number); + MaybeAppend(state, "}::"); + if (ParseOneCharToken(state, '_') && ParseName(state)) return true; + + // On late parse failure, roll back not only the input but also the output, + // whose trailing NUL was overwritten. + state->parse_state = copy; + if (state->parse_state.append) { + state->out[state->parse_state.out_cur_idx] = '\0'; + } + return false; + } + state->parse_state = copy; + + // ::= [] if (MaybeAppend(state, "::") && ParseName(state) && Optional(ParseDiscriminator(state))) { return true; } - - // Since we're not going to overwrite the above "::" by re-parsing the - // (whose trailing '\0' byte was in the byte now holding the - // first ':'), we have to rollback the "::" if the parse failed. + state->parse_state = copy; if (state->parse_state.append) { - state->out[state->parse_state.out_cur_idx - 2] = '\0'; + state->out[state->parse_state.out_cur_idx] = '\0'; } + // ::= s [] return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); } @@ -2023,12 +2397,22 @@ static bool ParseLocalName(State *state) { return false; } -// := _ <(non-negative) number> +// := _ +// := __ = 10)> _ static bool ParseDiscriminator(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; - if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { + + // Both forms start with _ so parse that first. + if (!ParseOneCharToken(state, '_')) return false; + + // + if (ParseDigit(state, nullptr)) return true; + + // _ _ + if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { return true; } state->parse_state = copy; diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.cc index 7086cab2..06ee7a4b 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.cc @@ -36,6 +36,34 @@ bool IsLower(char c) { return 'a' <= c && c <= 'z'; } bool IsUpper(char c) { return 'A' <= c && c <= 'Z'; } bool IsAlpha(char c) { return IsLower(c) || IsUpper(c); } bool IsIdentifierChar(char c) { return IsAlpha(c) || IsDigit(c) || c == '_'; } +bool IsLowerHexDigit(char c) { return IsDigit(c) || ('a' <= c && c <= 'f'); } + +const char* BasicTypeName(char c) { + switch (c) { + case 'a': return "i8"; + case 'b': return "bool"; + case 'c': return "char"; + case 'd': return "f64"; + case 'e': return "str"; + case 'f': return "f32"; + case 'h': return "u8"; + case 'i': return "isize"; + case 'j': return "usize"; + case 'l': return "i32"; + case 'm': return "u32"; + case 'n': return "i128"; + case 'o': return "u128"; + case 'p': return "_"; + case 's': return "i16"; + case 't': return "u16"; + case 'u': return "()"; + case 'v': return "..."; + case 'x': return "i64"; + case 'y': return "u64"; + case 'z': return "!"; + } + return nullptr; +} // Parser for Rust symbol mangling v0, whose grammar is defined here: // @@ -62,11 +90,12 @@ class RustSymbolParser { // Recursive-descent parsing is a beautifully readable translation of a // grammar, but it risks stack overflow if implemented by naive recursion on // the C++ call stack. So we simulate recursion by goto and switch instead, - // keeping a bounded stack of "return addresses" in the stack_ member. + // keeping a bounded stack of "return addresses" in the recursion_stack_ + // member. // // The callee argument is a statement label. We goto that label after - // saving the "return address" on stack_. The next continue statement in - // the for loop below "returns" from this "call". + // saving the "return address" on recursion_stack_. The next continue + // statement in the for loop below "returns" from this "call". // // The caller argument names the return point. Each value of caller must // appear in only one ABSL_DEMANGLER_RECURSE call and be listed in the @@ -80,9 +109,9 @@ class RustSymbolParser { // ParseIdentifier. #define ABSL_DEMANGLER_RECURSE(callee, caller) \ do { \ - if (depth_ == data_stack_pointer_) return false; \ + if (recursion_depth_ == kStackSize) return false; \ /* The next continue will switch on this saved value ... */ \ - stack_[depth_++] = caller; \ + recursion_stack_[recursion_depth_++] = caller; \ goto callee; \ /* ... and will land here, resuming the suspended code. */ \ case caller: {} \ @@ -92,10 +121,10 @@ class RustSymbolParser { // excessively complex input and infinite-loop bugs. int iter = 0; goto whole_encoding; - for (; iter < kMaxReturns && depth_ > 0; ++iter) { + for (; iter < kMaxReturns && recursion_depth_ > 0; ++iter) { // This switch resumes the code path most recently suspended by // ABSL_DEMANGLER_RECURSE. - switch (static_cast(stack_[--depth_])) { + switch (recursion_stack_[--recursion_depth_]) { // // symbol-name -> // _R decimal-number? path instantiating-crate? vendor-specific-suffix? @@ -115,15 +144,19 @@ class RustSymbolParser { // path -> crate-root | inherent-impl | trait-impl | trait-definition | // nested-path | generic-args | backref + // + // Note that ABSL_DEMANGLER_RECURSE does not work inside a nested switch + // (which would hide the generated case label). Thus we jump out of the + // inner switch with gotos before performing any fake recursion. path: switch (Take()) { case 'C': goto crate_root; - case 'M': return false; // inherent-impl not yet implemented - case 'X': return false; // trait-impl not yet implemented - case 'Y': return false; // trait-definition not yet implemented + case 'M': goto inherent_impl; + case 'X': goto trait_impl; + case 'Y': goto trait_definition; case 'N': goto nested_path; - case 'I': return false; // generic-args not yet implemented - case 'B': return false; // backref not yet implemented + case 'I': goto generic_args; + case 'B': goto path_backref; default: return false; } @@ -132,16 +165,54 @@ class RustSymbolParser { if (!ParseIdentifier()) return false; continue; - // nested-path -> N namespace path identifier (N consumed above) + // inherent-impl -> M impl-path type (M already consumed) + inherent_impl: + if (!Emit("<")) return false; + ABSL_DEMANGLER_RECURSE(impl_path, kInherentImplType); + ABSL_DEMANGLER_RECURSE(type, kInherentImplEnding); + if (!Emit(">")) return false; + continue; + + // trait-impl -> X impl-path type path (X already consumed) + trait_impl: + if (!Emit("<")) return false; + ABSL_DEMANGLER_RECURSE(impl_path, kTraitImplType); + ABSL_DEMANGLER_RECURSE(type, kTraitImplInfix); + if (!Emit(" as ")) return false; + ABSL_DEMANGLER_RECURSE(path, kTraitImplEnding); + if (!Emit(">")) return false; + continue; + + // impl-path -> disambiguator? path (but never print it!) + impl_path: + ++silence_depth_; + { + int ignored_disambiguator; + if (!ParseDisambiguator(ignored_disambiguator)) return false; + } + ABSL_DEMANGLER_RECURSE(path, kImplPathEnding); + --silence_depth_; + continue; + + // trait-definition -> Y type path (Y already consumed) + trait_definition: + if (!Emit("<")) return false; + ABSL_DEMANGLER_RECURSE(type, kTraitDefinitionInfix); + if (!Emit(" as ")) return false; + ABSL_DEMANGLER_RECURSE(path, kTraitDefinitionEnding); + if (!Emit(">")) return false; + continue; + + // nested-path -> N namespace path identifier (N already consumed) // namespace -> lower | upper nested_path: - // Uppercase namespaces must be saved on the stack so we can print + // Uppercase namespaces must be saved on a stack so we can print // ::{closure#0} or ::{shim:vtable#0} or ::{X:name#0} as needed. if (IsUpper(Peek())) { - if (!PushByte(static_cast(Take()))) return false; + if (!PushNamespace(Take())) return false; ABSL_DEMANGLER_RECURSE(path, kIdentifierInUppercaseNamespace); if (!Emit("::")) return false; - if (!ParseIdentifier(static_cast(PopByte()))) return false; + if (!ParseIdentifier(PopNamespace())) return false; continue; } @@ -156,6 +227,274 @@ class RustSymbolParser { // Neither upper or lower return false; + + // type -> basic-type | array-type | slice-type | tuple-type | + // ref-type | mut-ref-type | const-ptr-type | mut-ptr-type | + // fn-type | dyn-trait-type | path | backref + // + // We use ifs instead of switch (Take()) because the default case jumps + // to path, which will need to see the first character not yet Taken + // from the input. Because we do not use a nested switch here, + // ABSL_DEMANGLER_RECURSE works fine in the 'S' case. + type: + if (IsLower(Peek())) { + const char* type_name = BasicTypeName(Take()); + if (type_name == nullptr || !Emit(type_name)) return false; + continue; + } + if (Eat('A')) { + // array-type = A type const + if (!Emit("[")) return false; + ABSL_DEMANGLER_RECURSE(type, kArraySize); + if (!Emit("; ")) return false; + ABSL_DEMANGLER_RECURSE(constant, kFinishArray); + if (!Emit("]")) return false; + continue; + } + if (Eat('S')) { + if (!Emit("[")) return false; + ABSL_DEMANGLER_RECURSE(type, kSliceEnding); + if (!Emit("]")) return false; + continue; + } + if (Eat('T')) goto tuple_type; + if (Eat('R')) { + if (!Emit("&")) return false; + if (!ParseOptionalLifetime()) return false; + goto type; + } + if (Eat('Q')) { + if (!Emit("&mut ")) return false; + if (!ParseOptionalLifetime()) return false; + goto type; + } + if (Eat('P')) { + if (!Emit("*const ")) return false; + goto type; + } + if (Eat('O')) { + if (!Emit("*mut ")) return false; + goto type; + } + if (Eat('F')) goto fn_type; + if (Eat('D')) goto dyn_trait_type; + if (Eat('B')) goto type_backref; + goto path; + + // tuple-type -> T type* E (T already consumed) + tuple_type: + if (!Emit("(")) return false; + + // The toolchain should call the unit type u instead of TE, but the + // grammar and other demanglers also recognize TE, so we do too. + if (Eat('E')) { + if (!Emit(")")) return false; + continue; + } + + // A tuple with one element is rendered (type,) instead of (type). + ABSL_DEMANGLER_RECURSE(type, kAfterFirstTupleElement); + if (Eat('E')) { + if (!Emit(",)")) return false; + continue; + } + + // A tuple with two elements is of course (x, y). + if (!Emit(", ")) return false; + ABSL_DEMANGLER_RECURSE(type, kAfterSecondTupleElement); + if (Eat('E')) { + if (!Emit(")")) return false; + continue; + } + + // And (x, y, z) for three elements. + if (!Emit(", ")) return false; + ABSL_DEMANGLER_RECURSE(type, kAfterThirdTupleElement); + if (Eat('E')) { + if (!Emit(")")) return false; + continue; + } + + // For longer tuples we write (x, y, z, ...), printing none of the + // content of the fourth and later types. Thus we avoid exhausting + // output buffers and human readers' patience when some library has a + // long tuple as an implementation detail, without having to + // completely obfuscate all tuples. + if (!Emit(", ...)")) return false; + ++silence_depth_; + while (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(type, kAfterSubsequentTupleElement); + } + --silence_depth_; + continue; + + // fn-type -> F fn-sig (F already consumed) + // fn-sig -> binder? U? (K abi)? type* E type + // abi -> C | undisambiguated-identifier + // + // We follow the C++ demangler in suppressing details of function + // signatures. Every function type is rendered "fn...". + fn_type: + if (!Emit("fn...")) return false; + ++silence_depth_; + if (!ParseOptionalBinder()) return false; + (void)Eat('U'); + if (Eat('K')) { + if (!Eat('C') && !ParseUndisambiguatedIdentifier()) return false; + } + while (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(type, kContinueParameterList); + } + ABSL_DEMANGLER_RECURSE(type, kFinishFn); + --silence_depth_; + continue; + + // dyn-trait-type -> D dyn-bounds lifetime (D already consumed) + // dyn-bounds -> binder? dyn-trait* E + // + // The grammar strangely allows an empty trait list, even though the + // compiler should never output one. We follow existing demanglers in + // rendering DEL_ as "dyn ". + // + // Because auto traits lengthen a type name considerably without + // providing much value to a search for related source code, it would be + // desirable to abbreviate + // dyn main::Trait + std::marker::Copy + std::marker::Send + // to + // dyn main::Trait + ..., + // eliding the auto traits. But it is difficult to do so correctly, in + // part because there is no guarantee that the mangling will list the + // main trait first. So we just print all the traits in their order of + // appearance in the mangled name. + dyn_trait_type: + if (!Emit("dyn ")) return false; + if (!ParseOptionalBinder()) return false; + if (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(dyn_trait, kBeginAutoTraits); + while (!Eat('E')) { + if (!Emit(" + ")) return false; + ABSL_DEMANGLER_RECURSE(dyn_trait, kContinueAutoTraits); + } + } + if (!ParseRequiredLifetime()) return false; + continue; + + // dyn-trait -> path dyn-trait-assoc-binding* + // dyn-trait-assoc-binding -> p undisambiguated-identifier type + // + // We render nonempty binding lists as <>, omitting their contents as + // for generic-args. + dyn_trait: + ABSL_DEMANGLER_RECURSE(path, kContinueDynTrait); + if (Peek() == 'p') { + if (!Emit("<>")) return false; + ++silence_depth_; + while (Eat('p')) { + if (!ParseUndisambiguatedIdentifier()) return false; + ABSL_DEMANGLER_RECURSE(type, kContinueAssocBinding); + } + --silence_depth_; + } + continue; + + // const -> type const-data | p | backref + // + // const is a C++ keyword, so we use the label `constant` instead. + constant: + if (Eat('B')) goto const_backref; + if (Eat('p')) { + if (!Emit("_")) return false; + continue; + } + + // Scan the type without printing it. + // + // The Rust language restricts the type of a const generic argument + // much more than the mangling grammar does. We do not enforce this. + // + // We also do not bother printing false, true, 'A', and '\u{abcd}' for + // the types bool and char. Because we do not print generic-args + // contents, we expect to print constants only in array sizes, and + // those should not be bool or char. + ++silence_depth_; + ABSL_DEMANGLER_RECURSE(type, kConstData); + --silence_depth_; + + // const-data -> n? hex-digit* _ + // + // Although the grammar doesn't say this, existing demanglers expect + // that zero is 0, not an empty digit sequence, and no nonzero value + // may have leading zero digits. Also n0_ is accepted and printed as + // -0, though a toolchain will probably never write that encoding. + if (Eat('n') && !EmitChar('-')) return false; + if (!Emit("0x")) return false; + if (Eat('0')) { + if (!EmitChar('0')) return false; + if (!Eat('_')) return false; + continue; + } + while (IsLowerHexDigit(Peek())) { + if (!EmitChar(Take())) return false; + } + if (!Eat('_')) return false; + continue; + + // generic-args -> I path generic-arg* E (I already consumed) + // + // We follow the C++ demangler in omitting all the arguments from the + // output, printing only the list opening and closing tokens. + generic_args: + ABSL_DEMANGLER_RECURSE(path, kBeginGenericArgList); + if (!Emit("::<>")) return false; + ++silence_depth_; + while (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(generic_arg, kContinueGenericArgList); + } + --silence_depth_; + continue; + + // generic-arg -> lifetime | type | K const + generic_arg: + if (Peek() == 'L') { + if (!ParseOptionalLifetime()) return false; + continue; + } + if (Eat('K')) goto constant; + goto type; + + // backref -> B base-62-number (B already consumed) + // + // The BeginBackref call parses and range-checks the base-62-number. We + // always do that much. + // + // The recursive call parses and prints what the backref points at. We + // save CPU and stack by skipping this work if the output would be + // suppressed anyway. + path_backref: + if (!BeginBackref()) return false; + if (silence_depth_ == 0) { + ABSL_DEMANGLER_RECURSE(path, kPathBackrefEnding); + } + EndBackref(); + continue; + + // This represents the same backref production as in path_backref but + // parses the target as a type instead of a path. + type_backref: + if (!BeginBackref()) return false; + if (silence_depth_ == 0) { + ABSL_DEMANGLER_RECURSE(type, kTypeBackrefEnding); + } + EndBackref(); + continue; + + const_backref: + if (!BeginBackref()) return false; + if (silence_depth_ == 0) { + ABSL_DEMANGLER_RECURSE(constant, kConstantBackrefEnding); + } + EndBackref(); + continue; } } @@ -169,12 +508,52 @@ class RustSymbolParser { kVendorSpecificSuffix, kIdentifierInUppercaseNamespace, kIdentifierInLowercaseNamespace, + kInherentImplType, + kInherentImplEnding, + kTraitImplType, + kTraitImplInfix, + kTraitImplEnding, + kImplPathEnding, + kTraitDefinitionInfix, + kTraitDefinitionEnding, + kArraySize, + kFinishArray, + kSliceEnding, + kAfterFirstTupleElement, + kAfterSecondTupleElement, + kAfterThirdTupleElement, + kAfterSubsequentTupleElement, + kContinueParameterList, + kFinishFn, + kBeginAutoTraits, + kContinueAutoTraits, + kContinueDynTrait, + kContinueAssocBinding, + kConstData, + kBeginGenericArgList, + kContinueGenericArgList, + kPathBackrefEnding, + kTypeBackrefEnding, + kConstantBackrefEnding, }; - // Element count for the stack_ array. A larger kStackSize accommodates more + // Element counts for the stack arrays. Larger stack sizes accommodate more // deeply nested names at the cost of a larger footprint on the C++ call // stack. - enum { kStackSize = 256 }; + enum { + // Maximum recursive calls outstanding at one time. + kStackSize = 256, + + // Maximum N nested-paths open at once. We do not expect + // closures inside closures inside closures as much as functions inside + // modules inside other modules, so we can use a smaller array here. + kNamespaceStackSize = 64, + + // Maximum number of nested backrefs. We can keep this stack pretty small + // because we do not follow backrefs inside generic-args or other contexts + // that suppress printing, so deep stacking is unlikely in practice. + kPositionStackSize = 16, + }; // Returns the next input character without consuming it. char Peek() const { return encoding_[pos_]; } @@ -311,6 +690,20 @@ class RustSymbolParser { int disambiguator = 0; if (!ParseDisambiguator(disambiguator)) return false; + return ParseUndisambiguatedIdentifier(uppercase_namespace, disambiguator); + } + + // Consumes from the input an identifier with no preceding disambiguator, + // returning true on success. + // + // When ParseIdentifier calls this, it passes the N character and + // disambiguator value so that "{closure#42}" and similar forms can be + // rendered correctly. + // + // At other appearances of undisambiguated-identifier in the grammar, this + // treatment is not applicable, and the call site omits both arguments. + ABSL_MUST_USE_RESULT bool ParseUndisambiguatedIdentifier( + char uppercase_namespace = '\0', int disambiguator = 0) { // undisambiguated-identifier -> u? decimal-number _? bytes const bool is_punycoded = Eat('u'); if (!IsDigit(Peek())) return false; @@ -320,6 +713,8 @@ class RustSymbolParser { // Emit the beginnings of braced forms like {shim:vtable#0}. if (uppercase_namespace == '\0') { + // Decoding of Punycode is not yet implemented. For now we emit + // "{Punycode ...}" with the raw encoding inside. if (is_punycoded && !Emit("{Punycode ")) return false; } else { switch (uppercase_namespace) { @@ -383,25 +778,116 @@ class RustSymbolParser { return true; } - // Pushes byte onto the data stack (the right side of stack_) and returns - // true if stack_ is not full, else returns false. - ABSL_MUST_USE_RESULT bool PushByte(std::uint8_t byte) { - if (depth_ == data_stack_pointer_) return false; - stack_[--data_stack_pointer_] = byte; + // Consumes a binder of higher-ranked lifetimes if one is present. On success + // returns true and discards the encoded lifetime count. On parse failure + // returns false. + ABSL_MUST_USE_RESULT bool ParseOptionalBinder() { + // binder -> G base-62-number + if (!Eat('G')) return true; + int ignored_binding_count; + return ParseBase62Number(ignored_binding_count); + } + + // Consumes a lifetime if one is present. + // + // On success returns true and discards the lifetime index. We do not print + // or even range-check lifetimes because they are a finer detail than other + // things we omit from output, such as the entire contents of generic-args. + // + // On parse failure returns false. + ABSL_MUST_USE_RESULT bool ParseOptionalLifetime() { + // lifetime -> L base-62-number + if (!Eat('L')) return true; + int ignored_de_bruijn_index; + return ParseBase62Number(ignored_de_bruijn_index); + } + + // Consumes a lifetime just like ParseOptionalLifetime, but returns false if + // there is no lifetime here. + ABSL_MUST_USE_RESULT bool ParseRequiredLifetime() { + if (Peek() != 'L') return false; + return ParseOptionalLifetime(); + } + + // Pushes ns onto the namespace stack and returns true if the stack is not + // full, else returns false. + ABSL_MUST_USE_RESULT bool PushNamespace(char ns) { + if (namespace_depth_ == kNamespaceStackSize) return false; + namespace_stack_[namespace_depth_++] = ns; + return true; + } + + // Pops the last pushed namespace. Requires that the namespace stack is not + // empty (namespace_depth_ > 0). + char PopNamespace() { return namespace_stack_[--namespace_depth_]; } + + // Pushes position onto the position stack and returns true if the stack is + // not full, else returns false. + ABSL_MUST_USE_RESULT bool PushPosition(int position) { + if (position_depth_ == kPositionStackSize) return false; + position_stack_[position_depth_++] = position; return true; } - // Pops the last pushed data byte from stack_. Requires that the data stack - // is not empty (data_stack_pointer_ < kStackSize). - std::uint8_t PopByte() { return stack_[data_stack_pointer_++]; } + // Pops the last pushed input position. Requires that the position stack is + // not empty (position_depth_ > 0). + int PopPosition() { return position_stack_[--position_depth_]; } + + // Consumes a base-62-number denoting a backref target, pushes the current + // input position on the data stack, and sets the input position to the + // beginning of the backref target. Returns true on success. Returns false + // if parsing failed, the stack is exhausted, or the backref target position + // is out of range. + ABSL_MUST_USE_RESULT bool BeginBackref() { + // backref = B base-62-number (B already consumed) + // + // Reject backrefs that don't parse, overflow int, or don't point backward. + // If the offset looks fine, adjust it to account for the _R prefix. + int offset = 0; + const int offset_of_this_backref = + pos_ - 2 /* _R */ - 1 /* B already consumed */; + if (!ParseBase62Number(offset) || offset < 0 || + offset >= offset_of_this_backref) { + return false; + } + offset += 2; + + // Save the old position to restore later. + if (!PushPosition(pos_)) return false; + + // Move the input position to the backref target. + // + // Note that we do not check whether the new position points to the + // beginning of a construct matching the context in which the backref + // appeared. We just jump to it and see whether nested parsing succeeds. + // We therefore accept various wrong manglings, e.g., a type backref + // pointing to an 'l' character inside an identifier, which happens to mean + // i32 when parsed as a type mangling. This saves the complexity and RAM + // footprint of remembering which offsets began which kinds of + // substructures. Existing demanglers take similar shortcuts. + pos_ = offset; + return true; + } + + // Cleans up after a backref production by restoring the previous input + // position from the data stack. + void EndBackref() { pos_ = PopPosition(); } + + // The leftmost recursion_depth_ elements of recursion_stack_ contain the + // ReturnAddresses pushed by ABSL_DEMANGLER_RECURSE calls not yet completed. + ReturnAddress recursion_stack_[kStackSize] = {}; + int recursion_depth_ = 0; + + // The leftmost namespace_depth_ elements of namespace_stack_ contain the + // uppercase namespace identifiers for open nested-paths, e.g., 'C' for a + // closure. + char namespace_stack_[kNamespaceStackSize] = {}; + int namespace_depth_ = 0; - // Call and data stacks reside in stack_. The leftmost depth_ elements - // contain ReturnAddresses pushed by ABSL_DEMANGLER_RECURSE. The elements - // from index data_stack_pointer_ to the right edge of stack_ contain bytes - // pushed by PushByte. - std::uint8_t stack_[kStackSize] = {}; - int data_stack_pointer_ = kStackSize; - int depth_ = 0; + // The leftmost position_depth_ elements of position_stack_ contain the input + // positions to return to after fully printing the targets of backrefs. + int position_stack_[kPositionStackSize] = {}; + int position_depth_ = 0; // Anything parsed while silence_depth_ > 0 contributes nothing to the // demangled output. For constructs omitted from the demangling, such as @@ -409,8 +895,8 @@ class RustSymbolParser { // silence_depth_ on the way in and decrement silence_depth_ on the way out. int silence_depth_ = 0; - // Input: encoding_ points just after the _R in a Rust mangled symbol, and - // encoding_[pos_] is the next input character to be scanned. + // Input: encoding_ points to a Rust mangled symbol, and encoding_[pos_] is + // the next input character to be scanned. int pos_ = 0; const char* encoding_ = nullptr; diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.h b/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.h index 8e9060b4..1c19815f 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.h +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle_rust.h @@ -33,10 +33,8 @@ namespace debugging_internal { // call-stack space. It is suitable for symbolizing stack traces in a signal // handler. // -// The demangling logic is under development. In this version of Abseil, -// DemangleRustSymbolEncoding parses a few simple kinds of symbol names, but -// nothing having backreferences in the input or angle brackets in the -// demangling, and it emits raw Punycode instead of the UTF-8 represented by it. +// The demangling logic is under development; search for "not yet implemented" +// in the .cc file to see where the gaps are. bool DemangleRustSymbolEncoding(const char* mangled, char* out, std::size_t out_size); diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle_rust_test.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle_rust_test.cc index 2841576e..00bba780 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle_rust_test.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle_rust_test.cc @@ -209,6 +209,374 @@ TEST(DemangleRust, NestedUppercaseNamespaces) { "crate_name::{Y:y#2}::{X:x#3}::{closure#4}"); } +TEST(DemangleRust, TraitDefinition) { + EXPECT_DEMANGLING( + "_RNvYNtC7crate_a9my_structNtC7crate_b8my_trait1f", + "::f"); +} + +TEST(DemangleRust, BasicTypeNames) { + EXPECT_DEMANGLING("_RNvYaNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYbNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYcNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYdNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYeNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYfNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYhNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYiNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYjNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYlNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYmNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYnNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYoNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYpNtC1c1t1f", "<_ as c::t>::f"); + EXPECT_DEMANGLING("_RNvYsNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYtNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYuNtC1c1t1f", "<() as c::t>::f"); + EXPECT_DEMANGLING("_RNvYvNtC1c1t1f", "<... as c::t>::f"); + EXPECT_DEMANGLING("_RNvYxNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYyNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYzNtC1c1t1f", "::f"); + + EXPECT_DEMANGLING_FAILS("_RNvYkNtC1c1t1f"); +} + +TEST(DemangleRust, SliceTypes) { + EXPECT_DEMANGLING("_RNvYSlNtC1c1t1f", "<[i32] as c::t>::f"); + EXPECT_DEMANGLING("_RNvYSNtC1d1sNtC1c1t1f", "<[d::s] as c::t>::f"); +} + +TEST(DemangleRust, ImmutableReferenceTypes) { + EXPECT_DEMANGLING("_RNvYRlNtC1c1t1f", "<&i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYRNtC1d1sNtC1c1t1f", "<&d::s as c::t>::f"); +} + +TEST(DemangleRust, MutableReferenceTypes) { + EXPECT_DEMANGLING("_RNvYQlNtC1c1t1f", "<&mut i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYQNtC1d1sNtC1c1t1f", "<&mut d::s as c::t>::f"); +} + +TEST(DemangleRust, ConstantRawPointerTypes) { + EXPECT_DEMANGLING("_RNvYPlNtC1c1t1f", "<*const i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYPNtC1d1sNtC1c1t1f", "<*const d::s as c::t>::f"); +} + +TEST(DemangleRust, MutableRawPointerTypes) { + EXPECT_DEMANGLING("_RNvYOlNtC1c1t1f", "<*mut i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYONtC1d1sNtC1c1t1f", "<*mut d::s as c::t>::f"); +} + +TEST(DemangleRust, TupleLength0) { + EXPECT_DEMANGLING("_RNvYTENtC1c1t1f", "<() as c::t>::f"); +} + +TEST(DemangleRust, TupleLength1) { + EXPECT_DEMANGLING("_RNvYTlENtC1c1t1f", "<(i32,) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTNtC1d1sENtC1c1t1f", "<(d::s,) as c::t>::f"); +} + +TEST(DemangleRust, TupleLength2) { + EXPECT_DEMANGLING("_RNvYTlmENtC1c1t1f", "<(i32, u32) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTNtC1d1xNtC1e1yENtC1c1t1f", + "<(d::x, e::y) as c::t>::f"); +} + +TEST(DemangleRust, TupleLength3) { + EXPECT_DEMANGLING("_RNvYTlmnENtC1c1t1f", "<(i32, u32, i128) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTNtC1d1xNtC1e1yNtC1f1zENtC1c1t1f", + "<(d::x, e::y, f::z) as c::t>::f"); +} + +TEST(DemangleRust, LongerTuplesAbbreviated) { + EXPECT_DEMANGLING("_RNvYTlmnoENtC1c1t1f", + "<(i32, u32, i128, ...) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTlmnNtC1d1xNtC1e1yENtC1c1t1f", + "<(i32, u32, i128, ...) as c::t>::f"); +} + +TEST(DemangleRust, PathBackrefToCrate) { + EXPECT_DEMANGLING("_RNvYNtC8my_crate9my_structNtB4_8my_trait1f", + "::f"); +} + +TEST(DemangleRust, PathBackrefToNestedPath) { + EXPECT_DEMANGLING("_RNvYNtNtC1c1m1sNtB4_1t1f", "::f"); +} + +TEST(DemangleRust, PathBackrefAsInstantiatingCrate) { + EXPECT_DEMANGLING("_RNCNvC8my_crate7my_func0B3_", + "my_crate::my_func::{closure#0}"); +} + +TEST(DemangleRust, TypeBackrefsNestedInTuple) { + EXPECT_DEMANGLING("_RNvYTTRlB4_ERB3_ENtC1c1t1f", + "<((&i32, &i32), &(&i32, &i32)) as c::t>::f"); +} + +TEST(DemangleRust, NoInfiniteLoopOnBackrefToTheWhole) { + EXPECT_DEMANGLING_FAILS("_RB_"); + EXPECT_DEMANGLING_FAILS("_RNvB_1sNtC1c1t1f"); +} + +TEST(DemangleRust, NoCrashOnForwardBackref) { + EXPECT_DEMANGLING_FAILS("_RB0_"); + EXPECT_DEMANGLING_FAILS("_RB1_"); + EXPECT_DEMANGLING_FAILS("_RB2_"); + EXPECT_DEMANGLING_FAILS("_RB3_"); + EXPECT_DEMANGLING_FAILS("_RB4_"); +} + +TEST(DemangleRust, PathBackrefsDoNotRecurseDuringSilence) { + // B_ points at the value f (the whole mangling), so the cycle would lead to + // parse failure if the parser tried to parse what was pointed to. + EXPECT_DEMANGLING("_RNvYTlmnNtB_1sENtC1c1t1f", + "<(i32, u32, i128, ...) as c::t>::f"); +} + +TEST(DemangleRust, TypeBackrefsDoNotRecurseDuringSilence) { + // B2_ points at the tuple type, likewise making a cycle that the parser + // avoids following. + EXPECT_DEMANGLING("_RNvYTlmnB2_ENtC1c1t1f", + "<(i32, u32, i128, ...) as c::t>::f"); +} + +TEST(DemangleRust, ConstBackrefsDoNotRecurseDuringSilence) { + // B_ points at the whole I...E mangling, which does not parse as a const. + EXPECT_DEMANGLING("_RINvC1c1fAlB_E", "c::f::<>"); +} + +TEST(DemangleRust, ReturnFromBackrefToInputPosition256) { + // Show that we can resume at input positions that don't fit into a byte. + EXPECT_DEMANGLING("_RNvYNtC1c238very_long_type_" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABC" + "NtB4_1t1f", + "::f"); +} + +TEST(DemangleRust, EmptyGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fE", "c::f::<>"); +} + +TEST(DemangleRust, OneSimpleTypeInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1flE", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, OneTupleInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fTlmEE", // c::f::<(i32, u32)> + "c::f::<>"); +} + +TEST(DemangleRust, OnePathInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fNtC1d1sE", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, LongerGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1flmRNtC1d1sE", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, BackrefInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fRlB7_NtB2_1sE", // c::f::<&i32, &i32, c::s> + "c::f::<>"); +} + +TEST(DemangleRust, NestedGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fINtB2_1slEmE", // c::f::, u32> + "c::f::<>"); +} + +TEST(DemangleRust, MonomorphicEntityNestedInsideGeneric) { + EXPECT_DEMANGLING("_RNvINvC1c1fppE1g", // c::f::<_, _>::g + "c::f::<>::g"); +} + +TEST(DemangleRust, ArrayTypeWithSimpleElementType) { + EXPECT_DEMANGLING("_RNvYAlj1f_NtC1c1t1f", "<[i32; 0x1f] as c::t>::f"); +} + +TEST(DemangleRust, ArrayTypeWithComplexElementType) { + EXPECT_DEMANGLING("_RNvYAINtC1c1slEj1f_NtB6_1t1f", + "<[c::s::<>; 0x1f] as c::t>::f"); +} + +TEST(DemangleRust, NestedArrayType) { + EXPECT_DEMANGLING("_RNvYAAlj1f_j2e_NtC1c1t1f", + "<[[i32; 0x1f]; 0x2e] as c::t>::f"); +} + +TEST(DemangleRust, BackrefArraySize) { + EXPECT_DEMANGLING("_RNvYAAlj1f_B5_NtC1c1t1f", + "<[[i32; 0x1f]; 0x1f] as c::t>::f"); +} + +TEST(DemangleRust, ZeroArraySize) { + EXPECT_DEMANGLING("_RNvYAlj0_NtC1c1t1f", "<[i32; 0x0] as c::t>::f"); +} + +TEST(DemangleRust, SurprisingMinusesInArraySize) { + // Compilers shouldn't do this stuff, but existing demanglers accept it. + EXPECT_DEMANGLING("_RNvYAljn0_NtC1c1t1f", "<[i32; -0x0] as c::t>::f"); + EXPECT_DEMANGLING("_RNvYAljn42_NtC1c1t1f", "<[i32; -0x42] as c::t>::f"); +} + +TEST(DemangleRust, NumberAsGenericArg) { + EXPECT_DEMANGLING("_RINvC1c1fKl8_E", // c::f::<0x8> + "c::f::<>"); +} + +TEST(DemangleRust, NumberAsFirstOfTwoGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fKl8_mE", // c::f::<0x8, u32> + "c::f::<>"); +} + +TEST(DemangleRust, NumberAsSecondOfTwoGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fmKl8_E", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, NumberPlaceholder) { + EXPECT_DEMANGLING("_RNvINvC1c1fKpE1g", // c::f::<_>::g + "c::f::<>::g"); +} + +TEST(DemangleRust, InherentImplWithoutDisambiguator) { + EXPECT_DEMANGLING("_RNvMNtC8my_crate6my_modNtB2_9my_struct7my_func", + "::my_func"); +} + +TEST(DemangleRust, InherentImplWithDisambiguator) { + EXPECT_DEMANGLING("_RNvMs_NtC8my_crate6my_modNtB4_9my_struct7my_func", + "::my_func"); +} + +TEST(DemangleRust, TraitImplWithoutDisambiguator) { + EXPECT_DEMANGLING("_RNvXC8my_crateNtB2_9my_structNtB2_8my_trait7my_func", + "::my_func"); +} + +TEST(DemangleRust, TraitImplWithDisambiguator) { + EXPECT_DEMANGLING("_RNvXs_C8my_crateNtB4_9my_structNtB4_8my_trait7my_func", + "::my_func"); +} + +TEST(DemangleRust, TraitImplWithNonpathSelfType) { + EXPECT_DEMANGLING("_RNvXC8my_crateRlNtB2_8my_trait7my_func", + "<&i32 as my_crate::my_trait>::my_func"); +} + +TEST(DemangleRust, ThunkType) { + EXPECT_DEMANGLING("_RNvYFEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, NontrivialFunctionReturnType) { + EXPECT_DEMANGLING( + "_RNvYFERTlmENtC1c1t1f", // &(i32, u32) as c::t>::f + "::f"); +} + +TEST(DemangleRust, OneParameterType) { + EXPECT_DEMANGLING("_RNvYFlEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, TwoParameterTypes) { + EXPECT_DEMANGLING("_RNvYFlmEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, ExternC) { + EXPECT_DEMANGLING("_RNvYFKCEuNtC1c1t1f", // >::f + "::f"); +} + +TEST(DemangleRust, ExternOther) { + EXPECT_DEMANGLING( + "_RNvYFK5not_CEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, Unsafe) { + EXPECT_DEMANGLING("_RNvYFUEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, Binder) { + EXPECT_DEMANGLING( + // fn(&'a i32) -> &'a i32 as c::t>::f + "_RNvYFG_RL0_lEB5_NtC1c1t1f", + "::f"); +} + +TEST(DemangleRust, AllFnSigFeaturesInOrder) { + EXPECT_DEMANGLING( + // unsafe extern "C" fn(&'a i32) -> &'a i32 as c::t>::f + "_RNvYFG_UKCRL0_lEB8_NtC1c1t1f", + "::f"); +} + +TEST(DemangleRust, LifetimeInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fINtB2_1sL_EE", // c::f::> + "c::f::<>"); +} + +TEST(DemangleRust, EmptyDynTrait) { + // This shouldn't happen, but the grammar allows it and existing demanglers + // accept it. + EXPECT_DEMANGLING("_RNvYDEL_NtC1c1t1f", + "::f"); +} + +TEST(DemangleRust, SimpleDynTrait) { + EXPECT_DEMANGLING("_RNvYDNtC1c1tEL_NtC1d1u1f", + "::f"); +} + +TEST(DemangleRust, DynTraitWithOneAssociatedType) { + EXPECT_DEMANGLING( + "_RNvYDNtC1c1tp1xlEL_NtC1d1u1f", // as d::u>::f + " as d::u>::f"); +} + +TEST(DemangleRust, DynTraitWithTwoAssociatedTypes) { + EXPECT_DEMANGLING( + // as d::u>::f + "_RNvYDNtC1c1tp1xlp1ymEL_NtC1d1u1f", + " as d::u>::f"); +} + +TEST(DemangleRust, DynTraitPlusAutoTrait) { + EXPECT_DEMANGLING( + "_RNvYDNtC1c1tNtNtC3std6marker4SendEL_NtC1d1u1f", + "::f"); +} + +TEST(DemangleRust, DynTraitPlusTwoAutoTraits) { + EXPECT_DEMANGLING( + "_RNvYDNtC1c1tNtNtC3std6marker4CopyNtBc_4SyncEL_NtC1d1u1f", + "::f"); +} + +TEST(DemangleRust, HigherRankedDynTrait) { + EXPECT_DEMANGLING( + // c::t::<&'a i32> as d::u>::f + "_RNvYDG_INtC1c1tRL0_lEEL_NtC1d1u1f", + " as d::u>::f"); +} } // namespace } // namespace debugging_internal diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc index b6318302..de2d0979 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc @@ -150,6 +150,16 @@ TEST(Demangle, FunctionTemplateTemplateParamWithConstrainedArg) { EXPECT_STREQ(tmp, "foo<>()"); } +TEST(Demangle, ConstrainedAutoInFunctionTemplate) { + char tmp[100]; + + // template concept C = true; + // template void f() {} + // template void f<0>(); + ASSERT_TRUE(Demangle("_Z1fITnDk1CLi0EEvv", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "f<>()"); +} + TEST(Demangle, NonTemplateBuiltinType) { char tmp[100]; @@ -212,6 +222,209 @@ TEST(Demangle, TemplateParamSubstitutionWithGenericLambda) { EXPECT_STREQ(tmp, "Fooer<>::foo<>()"); } +TEST(Demangle, LambdaRequiresTrue) { + char tmp[100]; + + // auto $_0::operator()(int) const requires true + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QLb1E", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresSimpleExpression) { + char tmp[100]; + + // auto $_0::operator()(int) const requires 2 + 2 == 4 + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QeqplLi2ELi2ELi4E", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionContainingTrue) { + char tmp[100]; + + // auto $_0::operator()(int) const requires requires { true; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXLb1EE", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionContainingConcept) { + char tmp[100]; + + // auto $_0::operator()(int) const + // requires requires { std::same_as; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXsr3stdE7same_asIDtfp_EiEE", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionContainingNoexceptExpression) { + char tmp[100]; + + // auto $_0::operator()(int) const + // requires requires { {fp + fp} noexcept; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXplfp_fp_NE", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionContainingReturnTypeConstraint) { + char tmp[100]; + + // auto $_0::operator()(int) const + // requires requires { {fp + fp} -> std::same_as; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXplfp_fp_RNSt7same_asIDtfp_EEEE", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionWithBothNoexceptAndReturnType) { + char tmp[100]; + + // auto $_0::operator()(int) const + // requires requires { {fp + fp} noexcept -> std::same_as; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXplfp_fp_NRNSt7same_asIDtfp_EEEE", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionContainingType) { + char tmp[100]; + + // auto $_0::operator()(S) const + // requires requires { typename S::T; } + ASSERT_TRUE(Demangle("_ZNK3$_0clI1SEEDaT_QrqTNS2_1TEE", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionNestingAnotherRequires) { + char tmp[100]; + + // auto $_0::operator()(int) const requires requires { requires true; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqQLb1EE", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, LambdaRequiresRequiresExpressionContainingTwoRequirements) { + char tmp[100]; + + // auto $_0::operator()(int) const + // requires requires { requires true; requires 2 + 2 == 4; } + ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXLb1EXeqplLi2ELi2ELi4EE", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "$_0::operator()<>()"); +} + +TEST(Demangle, RequiresExpressionWithItsOwnParameter) { + char tmp[100]; + + // S f(int) + ASSERT_TRUE(Demangle("_Z1fIiE1SIXrQT__XplfL0p_fp_EEES1_", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "f<>()"); +} + +TEST(Demangle, LambdaWithExplicitTypeArgument) { + char tmp[100]; + + // Source: + // + // template T f(T t) { + // return [](U u) { return u + u; }(t); + // } + // + // template int f(int); + // + // Full LLVM demangling of the lambda call operator: + // + // auto int f(int)::'lambda'(int):: + // operator()(int) const + ASSERT_TRUE(Demangle("_ZZ1fIiET_S0_ENKUlTyS0_E_clIiEEDaS0_", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "f<>()::{lambda()#1}::operator()<>()"); +} + +TEST(Demangle, LambdaWithExplicitPackArgument) { + char tmp[100]; + + // Source: + // + // template T h(T t) { + // return [](U... u) { + // return ((u + u) + ... + 0); + // }(t); + // } + // + // template int h(int); + // + // Full LLVM demangling of the lambda call operator: + // + // auto int f(int)::'lambda'($T...):: + // operator()($T...) const + ASSERT_TRUE(Demangle("_ZZ1fIiET_S0_ENKUlTpTyDpT_E_clIJiEEEDaS2_", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "f<>()::{lambda()#1}::operator()<>()"); +} + +TEST(Demangle, LambdaInClassMemberDefaultArgument) { + char tmp[100]; + + // Source: + // + // struct S { + // static auto f(void (*g)() = [] {}) { return g; } + // }; + // void (*p)() = S::f(); + // + // Full LLVM demangling of the lambda call operator: + // + // S::f(void (*)())::'lambda'()::operator()() const + // + // Full GNU binutils demangling: + // + // S::f(void (*)())::{default arg#1}::{lambda()#1}::operator()() const + ASSERT_TRUE(Demangle("_ZZN1S1fEPFvvEEd_NKUlvE_clEv", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "S::f()::{default arg#1}::{lambda()#1}::operator()()"); + + // The same but in the second rightmost default argument. + ASSERT_TRUE(Demangle("_ZZN1S1fEPFvvEEd0_NKUlvE_clEv", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "S::f()::{default arg#2}::{lambda()#1}::operator()()"); + + // Reject negative <(parameter) number> values. + ASSERT_FALSE(Demangle("_ZZN1S1fEPFvvEEdn1_NKUlvE_clEv", tmp, sizeof(tmp))); +} + +TEST(Demangle, SubstpackNotationForTroublesomeTemplatePack) { + char tmp[100]; + + // Source: + // + // template