On Thu, 12 Feb 2026, Patrick Palka wrote:

> On Thu, 12 Feb 2026, Jason Merrill wrote:
> 
> > On 2/12/26 5:43 AM, Patrick Palka wrote:
> > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
> > > OK for trunk/15?
> > > 
> > > -- >8 --
> > > 
> > > The object argument of an xobj memfn call needs to be evaluated before
> > > its formal arguments, like with an iobj memfn call.  This patch
> > > generalizes the existing METHOD_TYPE callee evaluation order handling
> > > in cp_gimplify_expr to also handle xobj memfn callees.  (The handling
> > > needs to look at both fntype and decl because the callee can be e.g. an
> > > OBJ_TYPE_REF for which decl would be NULL.)
> > 
> > It seems to me that this sequencing is a property of the syntax "a.f ("
> > ([expr.call]/7) rather than of the function; it is not required for e.g.
> > "(*&A::f) (a, ++a.m)".  Though it is still allowed under "indeterminately
> > sequenced", so this is OK with an added comment.
> 
> AFAICT the METHOD_TYPE check is needed more for virtual function calls,
> e.g. m_fallback->seek(newPos) in g++.dg/pr121757.C, where the callee is
> expressed as OBJ_TYPE_REF:
> 
>   OBJ_TYPE_REF(*NON_LVALUE_EXPR <NON_LVALUE_EXPR <((struct 
> QQmlPreviewFileEngine *) 
> this)->m_fallback>->_vptr.QAbstractFileEngine>;(struct 
> QAbstractFileEngine)NON_LVALUE_EXPR <((struct QQmlPreviewFileEngine *) 
> this)->m_fallback>->0B)
>     (NON_LVALUE_EXPR <((struct QQmlPreviewFileEngine *) this)->m_fallback>, 
> newPos);
> 
> So I suppose we want to continue evaluating the callee first there,
> given the syntactic form.
> 
> For (&A::f)(a, ++a.m) we express it the same as a.f(++a.m):
> 
>   A::f (TARGET_EXPR <D.3029, a>,  ++a.m);
> 
> and so checking 'decl' will cover both forms.
> 
> We also express (a.*&A::g)(...) the same as a.g(...), so checking
> 'decl' will also cover both forms (checking METHOD_TYPE will too).

... unless of course the pointer-to-member callee is unknown!

I just noticed the cp_get_callee_fndecl_nofold (*expr_p) == NULL_TREE
logic earlier in cp_gimplify_expr, which IIUC will cause us evaluate
an unknown callee first and foremost.  But that's a separate question.

This is what I'm going to commit:

-- >8 --

Subject: [PATCH] c++: evaluation order of xobj memfn call [PR123989]

The object argument of an xobj memfn call needs to be evaluated before
its formal arguments, like with an iobj memfn call.  This patch
generalizes the existing METHOD_TYPE callee evaluation order handling
in cp_gimplify_expr to also handle xobj memfn callees.

        PR c++/123989

gcc/cp/ChangeLog:

        * cp-gimplify.cc (cp_gimplify_expr) <case CALL_EXPR>: Evaluate
        the object argument of an xobj memfn call first too.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp23/explicit-obj-eval-order.C: New test.

Reviewed-by: Jason Merrill <[email protected]>
---
 gcc/cp/cp-gimplify.cc                            |  9 ++++++++-
 .../g++.dg/cpp23/explicit-obj-eval-order.C       | 16 ++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-eval-order.C

diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 5ccfeefa6982..17d7cb4d9233 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -921,7 +921,14 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
          tree fntype = TREE_TYPE (CALL_EXPR_FN (*expr_p));
          if (INDIRECT_TYPE_P (fntype))
            fntype = TREE_TYPE (fntype);
-         if (TREE_CODE (fntype) == METHOD_TYPE)
+         tree decl = cp_get_callee_fndecl_nofold (*expr_p);
+         /* We can't just rely on 'decl' because virtual function callees
+            are expressed as OBJ_TYPE_REF.  Though checking for METHOD_TYPE
+            means we'll also sequence PMF calls, which is allowed under
+            "indeterimnately sequenced".  */
+         if (TREE_CODE (fntype) == METHOD_TYPE
+             || (decl && DECL_LANG_SPECIFIC (decl)
+                 && DECL_XOBJ_MEMBER_FUNCTION_P (decl)))
            {
              int nargs = call_expr_nargs (*expr_p);
              bool side_effects = false;
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-eval-order.C 
b/gcc/testsuite/g++.dg/cpp23/explicit-obj-eval-order.C
new file mode 100644
index 000000000000..7ce81f32cc4b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-eval-order.C
@@ -0,0 +1,16 @@
+// PR c++/123989
+// { dg-do run { target c++23 } }
+
+struct A {
+  int m = 42;
+
+  void f(this A self, int n) {
+    if (self.m != 42 || n != 43)
+      __builtin_abort();
+  }
+};
+
+int main() {
+  A a;
+  a.f(++a.m);
+}
-- 
2.53.0.80.g6fcee47852

Reply via email to