Skip to content

[libc++] Fix no-op shrink_to_fit for vector<bool> #120495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 21, 2025

Conversation

winner245
Copy link
Contributor

@winner245 winner245 commented Dec 18, 2024

This PR addresses an issue where the shrink_to_fit function in vector<bool> is effectively a no-op, meaning it will never shrink the capacity.

Fixes #122502.

@winner245 winner245 force-pushed the vector_bool_shrink_to_fit branch from 89af9e2 to 98c17a2 Compare December 19, 2024 02:51
@winner245 winner245 marked this pull request as ready for review December 19, 2024 13:33
@winner245 winner245 requested a review from a team as a code owner December 19, 2024 13:33
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 19, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 19, 2024

@llvm/pr-subscribers-libcxx

Author: Peng Liu (winner245)

Changes

The current implementation of vector&lt;bool&gt;::shrink_to_fit contains a logical error that prevents the function from doing anything practical, i.e., it will never shrink the capacity unless an arithmetic underflow happens. This is because the implementation checks the following condition

if (__external_cap_to_internal(size()) &gt; __cap_) {
  try {
    do_shrink();
  } catch (...) {
  }  
}

However, this condition is always false (with one exception), as the number of used internal words __external_cap_to_internal(size()) will never exceed the internal storage capacity (__cap_). The only exception for this condition to be true is when size() == 0, where the evaluation of __external_cap_to_internal(0) underflows and wraps around to become size_type(-1). But it is highly unlikely that this behavior was intended, because it relies on an arithmetic underflow. Even if it were intentional, a more straightforward approach would have been:

if (empty())
  deallocate();

which avoids the try/catch clause and becomes much cleaner and simpler.

While the current implementation technically conforms to the standard—since the shrink-to-fit request is non-binding—it appears to be a logical error to me, particularly since a similar logical error was found in the reserve() function of __split_buffer in #105681, which remained uncorrected until last month (#115735).

This PR addresses the issue by modifying shrink_to_fit to actually perform the necessary capacity reduction, ensuring it behaves as expected.


Full diff: https://github.com/llvm/llvm-project/pull/120495.diff

1 Files Affected:

  • (modified) libcxx/include/__vector/vector_bool.h (+4-2)
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 36eb7f350ac406..190ea2585b7821 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -841,11 +841,13 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::reserve(size_type _
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::shrink_to_fit() _NOEXCEPT {
-  if (__external_cap_to_internal(size()) > __cap_) {
+  if (__external_cap_to_internal(size()) < __cap_) {
 #if _LIBCPP_HAS_EXCEPTIONS
     try {
 #endif // _LIBCPP_HAS_EXCEPTIONS
-      vector(*this, allocator_type(__alloc_)).swap(*this);
+      vector __v(*this, allocator_type(__alloc_));
+      if (__v.__cap_ < __cap_)
+        __v.swap(*this);
 #if _LIBCPP_HAS_EXCEPTIONS
     } catch (...) {
     }

@winner245 winner245 force-pushed the vector_bool_shrink_to_fit branch from 98c17a2 to 3fb8884 Compare December 20, 2024 02:39
@winner245 winner245 changed the title [libc++] Fix shrink_to_fit implementation for vector<bool> [libc++] Fix no-op shrink_to_fit for vector<bool> Jan 1, 2025
@winner245 winner245 force-pushed the vector_bool_shrink_to_fit branch 3 times, most recently from f2e48f4 to a3d32ec Compare January 10, 2025 00:09
@winner245 winner245 force-pushed the vector_bool_shrink_to_fit branch from a3d32ec to 9d23aa3 Compare January 14, 2025 18:36
@asmok-g
Copy link

asmok-g commented Jan 15, 2025

Thanks for the fix. This fixes some google code broken by #120577.

@winner245
Copy link
Contributor Author

@asmok-g Thanks for your feedback! The previous change #120577 was an internal fix. I'm curious about how it caused issues with Google code, and how my current PR fixes that. Could you provide more details or context about the specific problems it created? This will help me understand the impact and ensure a comprehensive fix.

@asmok-g
Copy link

asmok-g commented Jan 17, 2025

We got HeapLeakChecker: memory leak reports. Running under asan for more details, the stack trace shows the leak is related to the allocation of a vector_bool. I unfortunately can only provide a repro next week, as I'm out today. But can we still carry on with this fix as is for the time being ?

@alexfh
Copy link
Contributor

alexfh commented Jan 18, 2025

@asmok-g Thanks for your feedback! The previous change #120577 was an internal fix. I'm curious about how it caused issues with Google code, and how my current PR fixes that. Could you provide more details or context about the specific problems it created? This will help me understand the impact and ensure a comprehensive fix.

I suspect that the issues we've seen may be caused by the same effect as described in #122502. If we find a distinct problem, we'll let you know.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a few comments, thanks a lot for fixing this!

@winner245 winner245 force-pushed the vector_bool_shrink_to_fit branch from 9d23aa3 to 1108295 Compare January 20, 2025 18:30
@ldionne ldionne added the pending-ci Merging the PR is only pending completion of CI label Jan 20, 2025
@ldionne ldionne merged commit 2d317d9 into llvm:main Jan 21, 2025
77 checks passed
@winner245 winner245 deleted the vector_bool_shrink_to_fit branch January 21, 2025 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. pending-ci Merging the PR is only pending completion of CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] shrink_to_fit never shrinks
5 participants