[PATCH] libstdc++: Make __gnu_debug::vector usable in constant expressions [PR109536]

Jonathan Wakely jwakely.gcc@gmail.com
Sat Dec 16 09:37:34 GMT 2023


On Sat, 16 Dec 2023 at 09:14, Jonathan Wakely wrote:
>
> On Sat, 16 Dec 2023 at 00:27, Patrick Palka wrote:
> >
> > On Wed, 6 Dec 2023, Jonathan Wakely wrote:
> >
> > > Any comments on this approach?
> > >
> > > -- >8 --
> > >
> > > This makes constexpr std::vector (mostly) work in Debug Mode. All safe
> > > iterator instrumentation and checking is disabled during constant
> > > evaluation, because it requires mutex locks and calls to non-inline
> > > functions defined in libstdc++.so. It should be OK to disable the safety
> > > checks, because most UB should be detected during constant evaluation
> > > anyway.
> > >
> > > We could try to enable the full checking in constexpr, but it would mean
> > > wrapping all the non-inline functions like _M_attach with an inline
> > > _M_constexpr_attach that does the iterator housekeeping inline without
> > > mutex locks when calling for constant evaluation, and calls the
> > > non-inline function at runtime. That could be done in future if we find
> > > that we've lost safety or useful checking by disabling the safe
> > > iterators.
> > >
> > > There are a few test failures in C++20 mode, which I'm unable to
> > > explain. The _Safe_iterator::operator++() member gives errors for using
> > > non-constexpr functions during constant evaluation, even though those
> > > functions are guarded by std::is_constant_evaluated() checks. The same
> > > code works fine for C++23 and up.
> >
> > AFAICT these C++20 test failures are really due to the variable
> > definition of non-literal type
> >
> > 381    __gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
> >
> > which were prohibited in a constexpr function (even if that code was
> > never executed) until C++23's P2242R3.
>
> Ah, I figured it was a core change but I couldn't recall which one. Thanks.
>
> > We can use an immediately invoked lambda to work around this:
> >
> > 381    [this] {
> > 382      __gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
> > 383      ++base();
> > 384    }();
> > 385    return *this;
>
> We'd need some #if as this code has to work for C++98. But that's doable.

The attached patch seems simpler, I'm testing it now.
-------------- next part --------------
commit 5d70c6c2965647077749a869e9cdbf7e91dba4c7
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat Dec 16 09:31:40 2023

    libstdc++: Fix errors for constexpr __gnu_debug::vector in C++23 [PR109536]
    
    In the commit log for r14-6553-g7d00a59229ee17 I noted some tests FAIL
    in C++20 mode. Patrick identified that they were due to the variable
    definitions of non-literal type __scoped_lock, which were prohibited in
    a constexpr function (even if that code was never executed) until
    C++23's P2242R3.
    
    We can move the problematic code into new non-constexpr functions that
    are not called during constant evaluation.
    
    There's also a warning about a constexpr _M_swap function which is never
    defined. That's simply because I added the _GLIBCXX20_CONSTEXPR macro on
    a member that doesn't need it.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/109536
            * include/debug/safe_base.h (_Safe_sequence_base::_M_swap):
            Remove _GLIBCXX20_CONSTEXPR from non-inline member function.
            * include/debug/safe_iterator.h (_Safe_iterator::_M_move_assign)

diff --git a/libstdc++-v3/include/debug/safe_base.h b/libstdc++-v3/include/debug/safe_base.h
index d9c17b52b48..1519ad809a4 100644
--- a/libstdc++-v3/include/debug/safe_base.h
+++ b/libstdc++-v3/include/debug/safe_base.h
@@ -268,7 +268,6 @@ namespace __gnu_debug
      *  operation is complete all iterators that originally referenced
      *  one container now reference the other container.
      */
-    _GLIBCXX20_CONSTEXPR
     void
     _M_swap(_Safe_sequence_base& __x) _GLIBCXX_USE_NOEXCEPT;
 
diff --git a/libstdc++-v3/include/debug/safe_iterator.h b/libstdc++-v3/include/debug/safe_iterator.h
index 26f008982f8..bde34e1f99c 100644
--- a/libstdc++-v3/include/debug/safe_iterator.h
+++ b/libstdc++-v3/include/debug/safe_iterator.h
@@ -295,7 +295,13 @@ namespace __gnu_debug
 	    base() = __x.base();
 	    return *this;
 	  }
+	_M_move_assign(std::move(__x));
+	return *this;
+      }
 
+      void
+      _M_move_assign(_Safe_iterator&& __x) noexcept
+      {
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
 			      || __x._M_value_initialized(),
 			      _M_message(__msg_copy_singular)
@@ -303,7 +309,7 @@ namespace __gnu_debug
 			      ._M_iterator(__x, "other"));
 
 	if (std::__addressof(__x) == this)
-	  return *this;
+	  return;
 
 	if (this->_M_sequence && this->_M_sequence == __x._M_sequence)
 	  {
@@ -320,7 +326,6 @@ namespace __gnu_debug
 
 	__x._M_detach();
 	__x.base() = _Iterator();
-	return *this;
       }
 #endif
 
@@ -370,17 +375,20 @@ namespace __gnu_debug
       operator++() _GLIBCXX_NOEXCEPT
       {
 	if (std::__is_constant_evaluated())
-	  {
-	    ++base();
-	    return *this;
-	  }
+	  ++base();
+	else
+	  _M_increment();
+	return *this;
+      }
 
+      void
+      _M_increment() _GLIBCXX_NOEXCEPT
+      {
 	_GLIBCXX_DEBUG_VERIFY(this->_M_incrementable(),
 			      _M_message(__msg_bad_inc)
 			      ._M_iterator(*this, "this"));
 	__gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
 	++base();
-	return *this;
       }
 
       /**
@@ -689,17 +697,20 @@ namespace __gnu_debug
       operator--() _GLIBCXX_NOEXCEPT
       {
 	if (std::__is_constant_evaluated())
-	  {
-	    --this->base();
-	    return *this;
-	  }
+	  --this->base();
+	else
+	  _M_decrement();
+	return *this;
+      }
 
+      void
+      _M_decrement() _GLIBCXX_NOEXCEPT
+      {
 	_GLIBCXX_DEBUG_VERIFY(this->_M_decrementable(),
 			      _M_message(__msg_bad_dec)
 			      ._M_iterator(*this, "this"));
 	__gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
 	--this->base();
-	return *this;
       }
 
       /**


More information about the Libstdc++ mailing list