c++: Fix up constexpr CLEANUP_POINT_EXPR and TRY_FINALLY_EXPR handling [PR97790]
authorJakub Jelinek <jakub@redhat.com>
Thu, 12 Nov 2020 09:46:04 +0000 (10:46 +0100)
committerJakub Jelinek <jakub@redhat.com>
Thu, 12 Nov 2020 09:46:04 +0000 (10:46 +0100)
As the testcase shows, CLEANUP_POINT_EXPR (and I think TRY_FINALLY_EXPR too)
suffer from the same problem that I was trying to fix in
r10-3597-g1006c9d4395a939820df76f37c7b085a4a1a003f
for CLEANUP_STMT, namely that if in the middle of the body expression of
those stmts is e.g. return stmt, goto, break or continue (something that
changes *jump_target and makes it start skipping stmts), we then skip the
cleanups too, which is not appropriate - the cleanups were either queued up
during the non-skipping execution of the body (for CLEANUP_POINT_EXPR), or
for TRY_FINALLY_EXPR are relevant already after entering the body block.

> Would it make sense to always use a NULL jump_target when evaluating
> cleanups?

I was afraid of that, especially for TRY_FINALLY_EXPR, but it seems that
during constexpr evaluation the cleanups will most often be just very simple
destructor calls (or calls to cleanup attribute functions).
Furthermore, for neither of these 3 tree codes we'll reach that code if
jump_target && *jump_target initially (there is a return NULL_TREE much
earlier for those except for trees that could embed labels etc. in it and
clearly these 3 don't count in that).

2020-11-12  Jakub Jelinek  <jakub@redhat.com>

PR c++/97790
* constexpr.c (cxx_eval_constant_expression) <case CLEANUP_POINT_EXPR,
case TRY_FINALLY_EXPR, case CLEANUP_STMT>: Don't pass jump_target to
cxx_eval_constant_expression when evaluating the cleanups.

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

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

index b6f9c43..e6ab5ee 100644 (file)
@@ -6018,8 +6018,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        /* Evaluate the cleanups.  */
        FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
          cxx_eval_constant_expression (ctx, cleanup, false,
-                                       non_constant_p, overflow_p,
-                                       jump_target);
+                                       non_constant_p, overflow_p);
       }
       break;
 
@@ -6030,29 +6029,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       if (!*non_constant_p)
        /* Also evaluate the cleanup.  */
        cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true,
-                                     non_constant_p, overflow_p,
-                                     jump_target);
+                                     non_constant_p, overflow_p);
       break;
 
     case CLEANUP_STMT:
-      {
-       tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
-       r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
-                                         non_constant_p, overflow_p,
-                                         jump_target);
-       if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
-         {
-           iloc_sentinel ils (loc);
-           /* Also evaluate the cleanup.  If we weren't skipping at the
-              start of the CLEANUP_BODY, change jump_target temporarily
-              to &initial_jump_target, so that even a return or break or
-              continue in the body doesn't skip the cleanup.  */
-           cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
-                                         non_constant_p, overflow_p,
-                                         jump_target ? &initial_jump_target
-                                         : NULL);
-         }
-      }
+      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+                                       non_constant_p, overflow_p,
+                                       jump_target);
+      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+       {
+         iloc_sentinel ils (loc);
+         /* Also evaluate the cleanup.  */
+         cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+                                       non_constant_p, overflow_p);
+       }
       break;
 
       /* These differ from cxx_eval_unary_expression in that this doesn't
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor9.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor9.C
new file mode 100644 (file)
index 0000000..975e5fc
--- /dev/null
@@ -0,0 +1,31 @@
+// PR c++/97790
+// { dg-do compile { target c++20 } }
+
+struct S
+{
+  int *d;
+  int n;
+  constexpr S () : d(new int[1]{}), n(1) {}
+  constexpr ~S () { delete [] d; }
+};
+
+constexpr S
+foo ()
+{
+  return S ();
+}
+
+constexpr int
+bar ()
+{
+  return foo ().n;
+}
+
+constexpr int
+baz ()
+{
+  return S ().n;
+}
+
+constexpr int a = baz ();
+constexpr int b = bar ();