https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jakub at gcc dot gnu.org,
                   |                            |jason at gcc dot gnu.org,
                   |                            |redi at gcc dot gnu.org

--- Comment #1 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
Just playing around a little bit, on:
struct S { constexpr S () : s (5) {}; int s; };
constexpr bool foo ()
{
  S *p = new S ();
  delete p;
  return true;
}
(so, trivial dtor rather than introducing constexpr dtors for now):

--- gcc/cp/constexpr.c.jj       2019-08-27 12:26:39.335884288 +0200
+++ gcc/cp/constexpr.c  2019-08-30 18:49:59.673689644 +0200
@@ -1666,6 +1666,34 @@ cxx_eval_call_expression (const constexp
                                           lval, non_constant_p, overflow_p);
   if (!DECL_DECLARED_CONSTEXPR_P (fun))
     {
+      if (cxx_dialect >= cxx2a
+          && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fun))
+         && CP_DECL_CONTEXT (fun) == global_namespace)
+       {
+         if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
+           {
+             const int nargs = call_expr_nargs (t);
+             tree sz = 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 == 0)
+                   sz = arg;
+               }
+             gcc_assert (sz);
+             tree type = build_array_type_nelts (char_type_node,
+                                                 tree_to_uhwi (sz));
+             tree var = build_decl (loc, VAR_DECL, NULL_TREE, type);
+             DECL_ARTIFICIAL (var) = 1;
+             return fold_convert (ptr_type_node, build_address (var));
+           }
+         else
+           /* FIXME */;
+       }
       if (!ctx->quiet)
        {
          if (!lambda_static_thunk_p (fun))
@@ -6243,7 +6271,12 @@ potential_constant_expression_1 (tree t,
                if (!DECL_DECLARED_CONSTEXPR_P (fun)
                    /* Allow any built-in function; if the expansion
                       isn't constant, we'll deal with that then.  */
-                   && !fndecl_built_in_p (fun))
+                   && !fndecl_built_in_p (fun)
+                   /* In C++2a, replaceable global allocation functions
+                      are constant expressions.  */
+                   && (cxx_dialect < cxx2a
+                       || !IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fun))
+                       || CP_DECL_CONTEXT (fun) != global_namespace))
                  {
                    if (flags & tf_error)
                      {

This fails because we represent the new expression as COMPOUND_EXPR which first
calls the allocation function (handled by the above code) and then constructs
the var (effectively through a reinterpret cast not marked as such), and fail
on that:
      r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0,
                                 &empty_base);
      if (r == NULL_TREE)
        {
          /* We couldn't fold to a constant value.  Make sure it's not
             something we should have been able to fold.  */
          tree sub = op0;
          STRIP_NOPS (sub);
          if (TREE_CODE (sub) == ADDR_EXPR)
            {
              gcc_assert (!same_type_ignoring_top_level_qualifiers_p
                          (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t)));
              /* DR 1188 says we don't have to deal with this.  */
              if (!ctx->quiet)
                error ("accessing value of %qE through a %qT glvalue in a "
                       "constant expression", build_fold_indirect_ref (sub),
                       TREE_TYPE (t));
              *non_constant_p = true;
              return t;
            }

          if (lval && op0 != orig_op0)
            return build1 (INDIRECT_REF, TREE_TYPE (t), op0);
          if (!lval)
            VERIFY_CONSTANT (t);
          return t;
        }

So, first of all, is it a good idea to represent the HEAP variables through
artifical VAR_DECLs?

I guess in the outermost constexpr evaluation context we'd need to track which
of those we have allocated and deallocated and do the checking that at the end
of outermost constexpr evaluation no allocations are left around, and that we
don't deallocate something that hasn't been allocated.

As we don't have the actual dynamic type they will have at the end of new
expression, should we mark them some way and either allow the first
cxx_fold_indirect_ref or the above code to change their type the first time
they are stored?

Is placement new ok in constexpr contexts or not?

Can the global replaceable allocation functions be marked constexpr by the user
and if so, what should happen in that case, shall they be still treated as
magic allocation functions, something else?

For the constexpr dtors, we need to make sure to mark trivial dtors as
constexpr, and default dtors as constexpr if they satisfy the rules, verify the
restrictions for user defined constexpr dtors, and check for constexpr dtor in
literal type check.  Plus guess for constexpr variables we need to at some
point verify they are constexpr destructible (but on a copy of the variable
value, such that changes to the value during destruction don't modify the
actual value of the variable we use normally).

Reply via email to