public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-2868] c++: constexpr std::construct_at on empty field [PR101663]
@ 2021-08-12  1:01 Patrick Palka
  0 siblings, 0 replies; only message in thread
From: Patrick Palka @ 2021-08-12  1:01 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:21fd62e5ca9967bba8f97fd6244a8c6a564c2146

commit r12-2868-g21fd62e5ca9967bba8f97fd6244a8c6a564c2146
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Aug 11 20:59:53 2021 -0400

    c++: constexpr std::construct_at on empty field [PR101663]
    
    Here during constexpr evaluation of
    
      std::construct_at(&a._M_value)
    
    we find ourselves in cxx_eval_store_expression where the target object
    is 'a._M_value' and the initializer is {}.  Since _M_value is an empty
    [[no_unique_address]] member we don't create a sub-CONSTRUCTOR for it,
    so we end up in the early exit code path for empty stores with mismatched
    types and trip over the assert therein
    
      gcc_assert (is_empty_class (TREE_TYPE (init)) && !lval);
    
    because lval is true.  The reason it's true is because the INIT_EXPR in
    question is the LHS of a COMPOUND_EXPR, and evaluation of the LHS is
    always performed with lval=true (to indicate there's no lvalue-to-rvalue
    conversion).
    
    This patch makes the code path in question handle the lval=true case
    appropriately rather than asserting.  In passing, it also consolidates
    the duplicate implementations of std::construct_at/destroy_at in some
    of the C++20 constexpr tests into a common header file.
    
            PR c++/101663
    
    gcc/cp/ChangeLog:
    
            * constexpr.c (cxx_eval_store_expression): Handle the lval=true
            case in the early exit code path for empty stores with mismatched
            types.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp2a/construct_at.h: New convenience header file that
            defines minimal implementations of std::construct_at/destroy_at,
            split out from ...
            * g++.dg/cpp2a/constexpr-new5.C: ... here.
            * g++.dg/cpp2a/constexpr-new6.C: Use the header.
            * g++.dg/cpp2a/constexpr-new14.C: Likewise.
            * g++.dg/cpp2a/constexpr-new20.C: New test.

Diff:
---
 gcc/cp/constexpr.c                           |  4 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-new14.C | 60 +-------------------------
 gcc/testsuite/g++.dg/cpp2a/constexpr-new20.C | 18 ++++++++
 gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C  | 60 +-------------------------
 gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C  | 64 +---------------------------
 gcc/testsuite/g++.dg/cpp2a/construct_at.h    | 62 +++++++++++++++++++++++++++
 6 files changed, 85 insertions(+), 183 deletions(-)

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 1af365d47b9..25d84a377d8 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -5588,8 +5588,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	   argument, which has the derived type rather than the base type.  In
 	   this situation, just evaluate the initializer and return, since
 	   there's no actual data to store.  */
-	  gcc_assert (is_empty_class (TREE_TYPE (init)) && !lval);
-	  return init;
+	  gcc_assert (is_empty_class (TREE_TYPE (init)));
+	  return lval ? target : init;
 	}
       CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init);
       TREE_CONSTANT (*valp) = TREE_CONSTANT (init);
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new14.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new14.C
index fd6f6075ef0..26037397b1d 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new14.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new14.C
@@ -1,65 +1,7 @@
 // PR c++/97195
 // { dg-do compile { target c++20 } }
 
-namespace std
-{
-  typedef __SIZE_TYPE__ size_t;
-
-  template <typename T>
-  struct allocator
-  {
-    constexpr allocator () noexcept {}
-
-    constexpr T *allocate (size_t n)
-    { return static_cast<T *> (::operator new (n * sizeof(T))); }
-
-    constexpr void
-    deallocate (T *p, size_t n)
-    { ::operator delete (p); }
-  };
-
-  template <typename T, typename U = T &&>
-  U __declval (int);
-  template <typename T>
-  T __declval (long);
-  template <typename T>
-  auto declval () noexcept -> decltype (__declval<T> (0));
-
-  template <typename T>
-  struct remove_reference
-  { typedef T type; };
-  template <typename T>
-  struct remove_reference<T &>
-  { typedef T type; };
-  template <typename T>
-  struct remove_reference<T &&>
-  { typedef T type; };
-
-  template <typename T>
-  constexpr T &&
-  forward (typename std::remove_reference<T>::type &t) noexcept
-  { return static_cast<T&&> (t); }
-
-  template<typename T>
-  constexpr T &&
-  forward (typename std::remove_reference<T>::type &&t) noexcept
-  { return static_cast<T&&> (t); }
-
-  template <typename T, typename... A>
-  constexpr auto
-  construct_at (T *l, A &&... a)
-  noexcept (noexcept (::new ((void *) 0) T (std::declval<A> ()...)))
-  -> decltype (::new ((void *) 0) T (std::declval<A> ()...))
-  { return ::new ((void *) l) T (std::forward<A> (a)...); }
-
-  template <typename T>
-  constexpr inline void
-  destroy_at (T *l)
-  { l->~T (); }
-}
-
-inline void *operator new (std::size_t, void *p) noexcept
-{ return p; }
+#include "construct_at.h"
 
 constexpr bool
 foo ()
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new20.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new20.C
new file mode 100644
index 00000000000..88bc4429a8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new20.C
@@ -0,0 +1,18 @@
+// PR c++/101663
+// { dg-do compile { target c++20 } }
+
+#include "construct_at.h"
+
+template <typename _Tp> struct __box {
+  [[no_unique_address]] _Tp _M_value;
+};
+
+struct Empty {};
+
+constexpr bool test() {
+  __box<Empty> a;
+  std::construct_at(&a._M_value);
+  return true;
+}
+
+static_assert(test());
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C
index 2bb407a4b51..eeaee969266 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C
@@ -1,65 +1,7 @@
 // P0784R7
 // { dg-do compile { target c++20 } }
 
-namespace std
-{
-  typedef __SIZE_TYPE__ size_t;
-
-  template <typename T>
-  struct allocator
-  {
-    constexpr allocator () noexcept {}
-
-    constexpr T *allocate (size_t n)
-    { return static_cast<T *> (::operator new (n * sizeof(T))); }
-
-    constexpr void
-    deallocate (T *p, size_t n)
-    { ::operator delete (p); }
-  };
-
-  template <typename T, typename U = T &&>
-  U __declval (int);
-  template <typename T>
-  T __declval (long);
-  template <typename T>
-  auto declval () noexcept -> decltype (__declval<T> (0));
-
-  template <typename T>
-  struct remove_reference
-  { typedef T type; };
-  template <typename T>
-  struct remove_reference<T &>
-  { typedef T type; };
-  template <typename T>
-  struct remove_reference<T &&>
-  { typedef T type; };
-
-  template <typename T>
-  constexpr T &&
-  forward (typename std::remove_reference<T>::type &t) noexcept
-  { return static_cast<T&&> (t); }
-
-  template<typename T>
-  constexpr T &&
-  forward (typename std::remove_reference<T>::type &&t) noexcept
-  { return static_cast<T&&> (t); }
-
-  template <typename T, typename... A>
-  constexpr auto
-  construct_at (T *l, A &&... a)
-  noexcept (noexcept (::new ((void *) 0) T (std::declval<A> ()...)))
-  -> decltype (::new ((void *) 0) T (std::declval<A> ()...))
-  { return ::new ((void *) l) T (std::forward<A> (a)...); }
-
-  template <typename T>
-  constexpr inline void
-  destroy_at (T *l)
-  { l->~T (); }
-}
-
-inline void *operator new (std::size_t, void *p) noexcept
-{ return p; }
+#include "construct_at.h"
 
 constexpr bool
 foo ()
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C
index d51bdbb8269..eeaee969266 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C
@@ -1,69 +1,7 @@
 // P0784R7
 // { dg-do compile { target c++20 } }
 
-namespace std
-{
-  inline namespace _8 { }
-  namespace _8 {
-
-    typedef __SIZE_TYPE__ size_t;
-
-    template <typename T>
-    struct allocator
-    {
-      constexpr allocator () noexcept {}
-
-      constexpr T *allocate (size_t n)
-      { return static_cast<T *> (::operator new (n * sizeof(T))); }
-
-      constexpr void
-      deallocate (T *p, size_t n)
-      { ::operator delete (p); }
-    };
-
-    template <typename T, typename U = T &&>
-    U __declval (int);
-    template <typename T>
-    T __declval (long);
-    template <typename T>
-    auto declval () noexcept -> decltype (__declval<T> (0));
-
-    template <typename T>
-    struct remove_reference
-    { typedef T type; };
-    template <typename T>
-    struct remove_reference<T &>
-    { typedef T type; };
-    template <typename T>
-    struct remove_reference<T &&>
-    { typedef T type; };
-
-    template <typename T>
-    constexpr T &&
-    forward (typename std::remove_reference<T>::type &t) noexcept
-    { return static_cast<T&&> (t); }
-
-    template<typename T>
-    constexpr T &&
-    forward (typename std::remove_reference<T>::type &&t) noexcept
-    { return static_cast<T&&> (t); }
-
-    template <typename T, typename... A>
-    constexpr auto
-    construct_at (T *l, A &&... a)
-    noexcept (noexcept (::new ((void *) 0) T (std::declval<A> ()...)))
-    -> decltype (::new ((void *) 0) T (std::declval<A> ()...))
-    { return ::new ((void *) l) T (std::forward<A> (a)...); }
-
-    template <typename T>
-    constexpr inline void
-    destroy_at (T *l)
-    { l->~T (); }
-  }
-}
-
-inline void *operator new (std::size_t, void *p) noexcept
-{ return p; }
+#include "construct_at.h"
 
 constexpr bool
 foo ()
diff --git a/gcc/testsuite/g++.dg/cpp2a/construct_at.h b/gcc/testsuite/g++.dg/cpp2a/construct_at.h
new file mode 100644
index 00000000000..27e92cbb28c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/construct_at.h
@@ -0,0 +1,62 @@
+// A minimal conforming implementation of std::construct_at/destroy_at,
+// used by some C++20 constexpr tests to avoid including all of <memory>.
+
+namespace std
+{
+  typedef __SIZE_TYPE__ size_t;
+
+  template <typename T>
+  struct allocator
+  {
+    constexpr allocator () noexcept {}
+
+    constexpr T *allocate (size_t n)
+    { return static_cast<T *> (::operator new (n * sizeof(T))); }
+
+    constexpr void
+    deallocate (T *p, size_t n)
+    { ::operator delete (p); }
+  };
+
+  template <typename T, typename U = T &&>
+  U __declval (int);
+  template <typename T>
+  T __declval (long);
+  template <typename T>
+  auto declval () noexcept -> decltype (__declval<T> (0));
+
+  template <typename T>
+  struct remove_reference
+  { typedef T type; };
+  template <typename T>
+  struct remove_reference<T &>
+  { typedef T type; };
+  template <typename T>
+  struct remove_reference<T &&>
+  { typedef T type; };
+
+  template <typename T>
+  constexpr T &&
+  forward (typename std::remove_reference<T>::type &t) noexcept
+  { return static_cast<T&&> (t); }
+
+  template<typename T>
+  constexpr T &&
+  forward (typename std::remove_reference<T>::type &&t) noexcept
+  { return static_cast<T&&> (t); }
+
+  template <typename T, typename... A>
+  constexpr auto
+  construct_at (T *l, A &&... a)
+  noexcept (noexcept (::new ((void *) 0) T (std::declval<A> ()...)))
+  -> decltype (::new ((void *) 0) T (std::declval<A> ()...))
+  { return ::new ((void *) l) T (std::forward<A> (a)...); }
+
+  template <typename T>
+  constexpr inline void
+  destroy_at (T *l)
+  { l->~T (); }
+}
+
+inline void *operator new (std::size_t, void *p) noexcept
+{ return p; }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-08-12  1:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-12  1:01 [gcc r12-2868] c++: constexpr std::construct_at on empty field [PR101663] Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).