Skip to content
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

[libc++] Fix ambiguous call to std::max in vector<bool> #119801

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

winner245
Copy link
Contributor

@winner245 winner245 commented Dec 13, 2024

This PR fixes an ambiguous call to std::max in vector<bool> and also improves similar usage of std::max within std::basic_string to prevent potential issues.

Closes #121713.

Copy link

github-actions bot commented Dec 13, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@winner245 winner245 force-pushed the fix-max-call branch 4 times, most recently from 4657768 to fe95a47 Compare December 13, 2024 03:12
@winner245 winner245 marked this pull request as ready for review December 16, 2024 16:32
@winner245 winner245 requested a review from a team as a code owner December 16, 2024 16:32
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 16, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 16, 2024

@llvm/pr-subscribers-libcxx

Author: Peng Liu (winner245)

Changes

This PR addresses a compilation error in vector&lt;bool&gt; that arises from integral promotion when using custom allocators with smaller integral types, such as std::uint16_t. It also optimizes similar code in std::basic_string to prevent potential issues related to integral promotions.

The following code snippet reproduces the bug in vector&lt;bool&gt;:

#include &lt;exception&gt;
#include &lt;iostream&gt;
#include &lt;limits&gt;
#include &lt;memory&gt;
#include &lt;vector&gt;

template &lt;typename T, typename SIZE_TYPE = std::size_t, typename DIFF_TYPE = std::ptrdiff_t&gt;
class CustomSizedAllocator {
  template &lt;typename U, typename Sz, typename Diff&gt;
  friend class CustomSizedAllocator;

public:
  using value_type                  = T;
  using size_type                   = SIZE_TYPE;
  using difference_type             = DIFF_TYPE;
  using propagate_on_container_swap = std::true_type;

  explicit CustomSizedAllocator(int i = 0) : data_(i) {}

  template &lt;typename U, typename Sz, typename Diff&gt;
  constexpr CustomSizedAllocator(const CustomSizedAllocator&lt;U, Sz, Diff&gt;&amp; a) noexcept : data_(a.data_) {}

  constexpr T* allocate(size_type n) {
    if (n &gt; max_size())
      throw std::bad_array_new_length();
    return std::allocator&lt;T&gt;().allocate(n);
  }

  constexpr void deallocate(T* p, size_type n) noexcept { std::allocator&lt;T&gt;().deallocate(p, n); }

  constexpr size_type max_size() const noexcept { return std::numeric_limits&lt;size_type&gt;::max() / sizeof(value_type); }

  int get() { return data_; }

private:
  int data_;

  constexpr friend bool operator==(const CustomSizedAllocator&amp; a, const CustomSizedAllocator&amp; b) {
    return a.data_ == b.data_;
  }
  constexpr friend bool operator!=(const CustomSizedAllocator&amp; a, const CustomSizedAllocator&amp; b) {
    return a.data_ != b.data_;
  }
};


int main() {
  using Alloc = CustomSizedAllocator&lt;bool, std::uint16_t, std::int16_t&gt;;
  std::vector&lt;bool, Alloc&gt; c{Alloc{1}};
  c.resize(10);

  return 0;
}

This code fails to compile in libc++, while it compiles successfully in other standard library implementations. The underlying issue is found in the following line of code within vector&lt;bool&gt;:

return std::max(2 * __cap, __align_it(__new_size));

where the first operand's result type is promoted to int, while the second operand retains the size_type type, which is std::uint16_t in this case. This mismatch in argument types for std::max&lt;_Tp&gt; leads to the failure of template type argument deduction.

The solution is straightforward: explicitly specify the template type for std::max to ensure consistent types for both operands.


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

4 Files Affected:

  • (modified) libcxx/include/__cxx03/string (+6-2)
  • (modified) libcxx/include/__cxx03/vector (+1-1)
  • (modified) libcxx/include/__vector/vector_bool.h (+1-1)
  • (modified) libcxx/include/string (+3-1)
diff --git a/libcxx/include/__cxx03/string b/libcxx/include/__cxx03/string
index c4431dcb04d41e..c29f74290bd416 100644
--- a/libcxx/include/__cxx03/string
+++ b/libcxx/include/__cxx03/string
@@ -2483,7 +2483,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
     __throw_length_error();
   pointer __old_p = __get_pointer();
   size_type __cap =
-      __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
+      __old_cap < __ms / 2 - __alignment
+          ? __recommend(std::max<size_type>(__old_cap + __delta_cap, 2 * __old_cap))
+          : __ms - 1;
   __annotate_delete();
   auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
   pointer __p       = __allocation.ptr;
@@ -2526,7 +2528,9 @@ _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Trait
     __throw_length_error();
   pointer __old_p = __get_pointer();
   size_type __cap =
-      __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
+      __old_cap < __ms / 2 - __alignment
+          ? __recommend(std::max<size_type>(__old_cap + __delta_cap, 2 * __old_cap))
+          : __ms - 1;
   __annotate_delete();
   auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
   pointer __p       = __allocation.ptr;
diff --git a/libcxx/include/__cxx03/vector b/libcxx/include/__cxx03/vector
index 6ee35b4e36258f..80da74ded67b7a 100644
--- a/libcxx/include/__cxx03/vector
+++ b/libcxx/include/__cxx03/vector
@@ -2328,7 +2328,7 @@ vector<bool, _Allocator>::__recommend(size_type __new_size) const {
   const size_type __cap = capacity();
   if (__cap >= __ms / 2)
     return __ms;
-  return std::max(2 * __cap, __align_it(__new_size));
+  return std::max<size_type>(2 * __cap, __align_it(__new_size));
 }
 
 //  Default constructs __n objects starting at __end_
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 36eb7f350ac406..26b172af9138db 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -527,7 +527,7 @@ vector<bool, _Allocator>::__recommend(size_type __new_size) const {
   const size_type __cap = capacity();
   if (__cap >= __ms / 2)
     return __ms;
-  return std::max(2 * __cap, __align_it(__new_size));
+  return std::max<size_type>(2 * __cap, __align_it(__new_size));
 }
 
 //  Default constructs __n objects starting at __end_
diff --git a/libcxx/include/string b/libcxx/include/string
index 17bf4b3b98bf34..911b67cfcf26e8 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2518,7 +2518,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
     __throw_length_error();
   pointer __old_p = __get_pointer();
   size_type __cap =
-      __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
+      __old_cap < __ms / 2 - __alignment
+          ? __recommend(std::max<size_type>(__old_cap + __delta_cap, 2 * __old_cap))
+          : __ms - 1;
   __annotate_delete();
   auto __guard      = std::__make_scope_guard(__annotate_new_size(*this));
   auto __allocation = std::__allocate_at_least(__alloc_, __cap + 1);

@winner245 winner245 changed the title [libc++][bug] Fix compilation error in vector<bool> due to integral promotion [libc++][bug] Fix a bug in vector<bool> due to misuse of std::min Dec 31, 2024
@winner245 winner245 changed the title [libc++][bug] Fix a bug in vector<bool> due to misuse of std::min [libc++][bug] Fix a bug in vector<bool> due to misuse of std::max Dec 31, 2024
@winner245 winner245 changed the title [libc++][bug] Fix a bug in vector<bool> due to misuse of std::max [libc++][bug] Fix misuse of std::max in vector<bool> Dec 31, 2024
@winner245 winner245 changed the title [libc++][bug] Fix misuse of std::max in vector<bool> [libc++] Fix ambiguous call to std::max in vector<bool> Jan 5, 2025
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] Ambiguous call to std::max leads to complication failure
2 participants