- 
                Notifications
    
You must be signed in to change notification settings  - Fork 15.1k
 
          [libc++] Fix basic_string::shrink_to_fit for constant evaluation
          #142712
        
          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 basic_string::shrink_to_fit for constant evaluation
  
  #142712
              Conversation
Currently, when the string shrink into the SSO buffer, the lifetime of the buffer hasn't begun before copying characters into it, so the subsequent copy operation raises UB and thus causes constant evaluation failure. The existing test coverage seems a bit defective - `shrink_to_fit` is called on the copy of string after erasure, not the original string object. This PR reorders the `__set_short_size` call, which starts the lifetime of the SSO buffer, before the copy operation. Test coverage is achieved by calling `shrink_to_fit` on the original erased string.
| 
          
 @llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) ChangesCurrently, when the string shrink into the SSO buffer, the lifetime of the buffer hasn't begun before copying characters into it, so the subsequent copy operation raises UB and thus causes constant evaluation failure. The existing test coverage seems a bit defective  -  This PR reorders the  Full diff: https://github.com/llvm/llvm-project/pull/142712.diff 2 Files Affected: 
 diff --git a/libcxx/include/string b/libcxx/include/string
index 4f05e211919f3..67c4c537eb3b5 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -3434,8 +3434,8 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocat
 
   if (__fits_in_sso(__target_capacity)) {
     __annotation_guard __g(*this);
-    traits_type::copy(std::__to_address(__get_short_pointer()), std::__to_address(__ptr), __size + 1);
     __set_short_size(__size);
+    traits_type::copy(std::__to_address(__get_short_pointer()), std::__to_address(__ptr), __size + 1);
     __alloc_traits::deallocate(__alloc_, __ptr, __cap);
     return;
   }
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp
index 9ca7825a4ba92..a3b16c8da16cb 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp
@@ -19,7 +19,7 @@
 #include "test_macros.h"
 
 template <class S>
-TEST_CONSTEXPR_CXX20 void test(S s) {
+TEST_CONSTEXPR_CXX20 void test(S& s) {
   typename S::size_type old_cap = s.capacity();
   S s0                          = s;
   s.shrink_to_fit();
 | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, when the string shrink into the SSO buffer, the lifetime of the buffer hasn't begun before copying characters into it, so the subsequent copy operation raises UB and thus causes constant evaluation failure.
This is incorrect. It's not UB, since we know that CharT is an implicit lifetime type, so we start the lifetime when copying the elements.
LGTM with updated commit message.
          
 Yeah. Changed to the following. Is it OK enough? 
  | 
    
          
 Does the error occur in   | 
    
          
 Oops, I was confused and wrong. The error can be reproduced here: https://godbolt.org/z/MjMsf488W. 
  | 
    
Currently, when the string shrink into the SSO buffer, the
__rep_.__smember isn't activated before thetraits_type::copycallyet, so internal
__builtin_memmovecall writing to the buffer causes constant evaluation failure. The existing test coverage seems a bit defective and doesn't cover this case -shrink_to_fitis called on the copy of string after erasure, not the original string object.This PR reorders the
__set_short_sizecall, which starts the lifetime of the SSO buffer, before the copy operation. Test coverage is achieved by callingshrink_to_fiton the original erased string.