Hi!

C++17 and later say
"The postfix-expression is sequenced before each expression in the 
expression-list
and any default argument."
As the following testcase shows, we don't honor that.  Either
cp_gimplify_expr gimplifies some arguments (if CALL_EXPR_REVERSE_ARGS
or CALL_EXPR_ORDERED_ARGS or for !CALL_EXPR_OPERATOR_SYNTAX) and then
the postfix-expression's side-effects are evaluated after those arguments
that are pregimplified by cp_gimplify_expr, or, even if cp_gimplify_expr
doesn't do anything, gimplify_call_expr will:
  /* There is a sequence point before the call, so any side effects in
     the calling expression must occur before the actual call.  Force
     gimplify_expr to use an internal post queue.  */
  ret = gimplify_expr (&CALL_EXPR_FN (*expr_p), pre_p, NULL,
                       is_gimple_call_addr, fb_rvalue);
before gimplifying args, which is fine if there are side-effects, but
doesn't help the actual testcase, because say a user VAR_DECL or PARM_DECL
or RESULT_DECL with pointer to function type satisfies is_gimple_call_addr
predicate, so it will actually be evaluated only later after the arguments
are evaluated, which can actually overwrite the VAR_DECL etc.

So, the following patch gimplifies the postfix expression (CALL_EXPR_FN)
early, and if it isn't invariant or TYPE_OBJ_REF or SSA_NAME (in that case
we don't have to worry about something overwriting it), forces it into a
temporary.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-10-03  Jakub Jelinek  <ja...@redhat.com>

        PR c++/91974
        * cp-gimplify.c (cp_gimplify_expr) <case CALL_EXPR>: For
        -fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated
        before any arguments.  Additionally, ensure CALL_EXPR_FN that isn't
        invariant nor OBJ_TYPE_REF nor SSA_NAME is forced into a temporary.

        * g++.dg/cpp1z/eval-order5.C: New test.

--- gcc/cp/cp-gimplify.c.jj     2019-10-03 00:26:22.184804539 +0200
+++ gcc/cp/cp-gimplify.c        2019-10-03 10:01:50.769221516 +0200
@@ -818,6 +818,21 @@ cp_gimplify_expr (tree *expr_p, gimple_s
 
     case CALL_EXPR:
       ret = GS_OK;
+      if (flag_strong_eval_order == 2
+         && CALL_EXPR_FN (*expr_p)
+         && cp_get_callee_fndecl_nofold (*expr_p) == NULL_TREE)
+       {
+         enum gimplify_status t
+           = gimplify_expr (&CALL_EXPR_FN (*expr_p), pre_p, NULL,
+                            is_gimple_call_addr, fb_rvalue);
+         if (t == GS_ERROR)
+           ret = GS_ERROR;
+         else if (is_gimple_variable (CALL_EXPR_FN (*expr_p))
+                  && TREE_CODE (CALL_EXPR_FN (*expr_p)) != SSA_NAME)
+           CALL_EXPR_FN (*expr_p)
+             = get_initialized_tmp_var (CALL_EXPR_FN (*expr_p), pre_p,
+                                        NULL);
+       }
       if (!CALL_EXPR_FN (*expr_p))
        /* Internal function call.  */;
       else if (CALL_EXPR_REVERSE_ARGS (*expr_p))
--- gcc/testsuite/g++.dg/cpp1z/eval-order5.C.jj 2019-10-03 10:37:13.442990472 
+0200
+++ gcc/testsuite/g++.dg/cpp1z/eval-order5.C    2019-10-03 10:38:31.082812256 
+0200
@@ -0,0 +1,31 @@
+// PR c++/91974
+// { dg-do run }
+// { dg-options "-fstrong-eval-order" }
+
+extern "C" void abort ();
+
+bool ok = false;
+
+void
+foo (int x)
+{
+  if (x != 0)
+    abort ();
+  ok = true;
+}
+
+void
+bar (int)
+{
+  abort ();
+}
+
+int
+main ()
+{
+  typedef void (*T) (int);
+  T fn = foo;
+  fn ((fn = bar, 0));
+  if (fn != bar || !ok)
+    abort ();
+}

        Jakub

Reply via email to