PR c++/91369 - Implement P0784R7: constexpr new
authorJakub Jelinek <jakub@redhat.com>
Wed, 30 Oct 2019 21:55:12 +0000 (22:55 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 30 Oct 2019 21:55:12 +0000 (22:55 +0100)
PR c++/91369 - Implement P0784R7: constexpr new
* constexpr.c (cxx_replaceable_global_alloc_fn): Don't return true
for placement new.
(cxx_placement_new_fn, is_std_construct_at): New functions.
(cxx_eval_call_expression): Allow placement new in std::construct_at.
(potential_constant_expression_1): Likewise.

* g++.dg/cpp2a/constexpr-new5.C: New test.

From-SVN: r277649

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C [new file with mode: 0644]

index 46b8fe02907dffab3b96cefc50343037966b00aa..efb135e385d1fffb95e6b92671d2e8b6b758bd62 100644 (file)
@@ -1,5 +1,12 @@
 2019-10-30  Jakub Jelinek  <jakub@redhat.com>
 
+       PR c++/91369 - Implement P0784R7: constexpr new
+       * constexpr.c (cxx_replaceable_global_alloc_fn): Don't return true
+       for placement new.
+       (cxx_placement_new_fn, is_std_construct_at): New functions.
+       (cxx_eval_call_expression): Allow placement new in std::construct_at.
+       (potential_constant_expression_1): Likewise.
+
        * typeck.c (decl_in_std_namespace_p): Return true also for decls
        in inline namespaces inside of std namespace.
 
index 6b4e854e35c31a94d4104edc62fa589799db3acb..75db0b3c72deaf6113ae43daafc8cce9f303c6e9 100644 (file)
@@ -1601,7 +1601,41 @@ cxx_replaceable_global_alloc_fn (tree fndecl)
 {
   return (cxx_dialect >= cxx2a
          && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fndecl))
-         && CP_DECL_CONTEXT (fndecl) == global_namespace);
+         && CP_DECL_CONTEXT (fndecl) == global_namespace
+         && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fndecl)
+             || DECL_IS_OPERATOR_DELETE_P (fndecl)));
+}
+
+/* Return true if FNDECL is a placement new function that should be
+   useable during constant expression evaluation of std::construct_at.  */
+
+static inline bool
+cxx_placement_new_fn (tree fndecl)
+{
+  if (cxx_dialect >= cxx2a
+      && IDENTIFIER_NEW_OP_P (DECL_NAME (fndecl))
+      && CP_DECL_CONTEXT (fndecl) == global_namespace
+      && !DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fndecl)
+      && TREE_CODE (TREE_TYPE (fndecl)) == FUNCTION_TYPE)
+    {
+      tree first_arg = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
+      if (TREE_VALUE (first_arg) == ptr_type_node
+         && TREE_CHAIN (first_arg) == void_list_node)
+       return true;
+    }
+  return false;
+}
+
+/* Return true if FNDECL is std::construct_at.  */
+
+static inline bool
+is_std_construct_at (tree fndecl)
+{
+  if (!decl_in_std_namespace_p (fndecl))
+    return false;
+
+  tree name = DECL_NAME (fndecl);
+  return name && id_equal (name, "construct_at");
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -1738,6 +1772,27 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
              return t;
            }
        }
+      /* Allow placement new in std::construct_at, just return the second
+        argument.  */
+      if (cxx_placement_new_fn (fun)
+         && ctx->call
+         && ctx->call->fundef
+         && is_std_construct_at (ctx->call->fundef->decl))
+       {
+         const int nargs = call_expr_nargs (t);
+         tree arg1 = NULL_TREE;
+         for (int i = 0; i < nargs; ++i)
+           {
+             tree arg = CALL_EXPR_ARG (t, i);
+             arg = cxx_eval_constant_expression (ctx, arg, false,
+                                                 non_constant_p, overflow_p);
+             VERIFY_CONSTANT (arg);
+             if (i == 1)
+               arg1 = arg;
+           }
+         gcc_assert (arg1);
+         return arg1;
+       }
       if (!ctx->quiet)
        {
          if (!lambda_static_thunk_p (fun))
@@ -6453,7 +6508,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                    && !fndecl_built_in_p (fun)
                    /* In C++2a, replaceable global allocation functions
                       are constant expressions.  */
-                   && !cxx_replaceable_global_alloc_fn (fun))
+                   && !cxx_replaceable_global_alloc_fn (fun)
+                   /* Allow placement new in std::construct_at.  */
+                   && (!cxx_placement_new_fn (fun)
+                       || current_function_decl == NULL_TREE
+                       || !is_std_construct_at (current_function_decl)))
                  {
                    if (flags & tf_error)
                      {
index d64275b8ea56dbc7f2ac98902669362b25e9735d..fe2c48acf5c6debbef6cf0f8342e57149bade254 100644 (file)
@@ -1,5 +1,8 @@
 2019-10-30  Jakub Jelinek  <jakub@redhat.com>
 
+       PR c++/91369 - Implement P0784R7: constexpr new
+       * g++.dg/cpp2a/constexpr-new5.C: New test.
+
        * g++.dg/cpp0x/Wpessimizing-move6.C: New test.
 
 2019-10-30  Bernd Edlinger  <bernd.edlinger@hotmail.de>
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C
new file mode 100644 (file)
index 0000000..b2b65f2
--- /dev/null
@@ -0,0 +1,79 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+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; }
+
+constexpr bool
+foo ()
+{
+  std::allocator<int> a;
+  auto p = a.allocate (2);
+  std::construct_at (p, 1);
+  std::construct_at (p + 1, 2);
+  if (p[0] != 1 || p[1] != 2)
+    throw 1;
+  std::destroy_at (p);
+  std::destroy_at (p + 1);
+  a.deallocate (p, 2);
+  return true;
+}
+
+static_assert (foo ());