Hi,

Vincenzo reported today that the work done by Roberto on devirtualizing final methods, doesn't cover operators (of all sorts). Thus I prepared the below, which passes the testsuite on x86_64-linux.

Is it Ok?

Thanks,
Paolo.

/////////////////////////
/cp
2012-05-03  Paolo Carlini  <paolo.carl...@oracle.com>

        PR c++/53186
        * call.c (build_over_call_maybe_devirtualize): New.
        (build_op_call_1, build_new_op_1, convert_like_real): Use it.

/testsuite
2012-05-03  Paolo Carlini  <paolo.carl...@oracle.com>

        PR c++/53186
        * g++.dg/other/final2.C: New.


Index: cp/call.c
===================================================================
--- cp/call.c   (revision 187058)
+++ cp/call.c   (working copy)
@@ -4031,6 +4031,26 @@ build_operator_new_call (tree fnname, VEC(tree,gc)
    return build_over_call (cand, LOOKUP_NORMAL, complain);
 }
 
+/* Variant of build_over_call which checks if the call can be 
+   devirtualized.  */
+
+static tree
+build_over_call_maybe_devirtualize (struct z_candidate *cand,
+                                   tsubst_flags_t complain)
+{
+  tree fn = cand->fn;
+  tree fntype = TREE_TYPE (fn);
+  int flags = LOOKUP_NORMAL;
+
+  if (DECL_FINAL_P (fn)
+      || (TREE_CODE (fntype) == METHOD_TYPE
+         && CLASSTYPE_FINAL (TYPE_METHOD_BASETYPE (fntype))))
+    /* We can devirtualize.  */
+    flags |= LOOKUP_NONVIRTUAL;
+
+  return build_over_call (cand, flags, complain);
+}
+
 /* Build a new call to operator().  This may change ARGS.  */
 
 static tree
@@ -4149,7 +4169,7 @@ build_op_call_1 (tree obj, VEC(tree,gc) **args, ts
         DECL_NAME here.  */
       else if (TREE_CODE (cand->fn) == FUNCTION_DECL
               && DECL_OVERLOADED_OPERATOR_P (cand->fn) == CALL_EXPR)
-       result = build_over_call (cand, LOOKUP_NORMAL, complain);
+       result = build_over_call_maybe_devirtualize (cand, complain);
       else
        {
          obj = convert_like_with_context (cand->convs[0], obj, cand->fn, -1,
@@ -5163,7 +5183,7 @@ build_new_op_1 (enum tree_code code, int flags, tr
          if (resolve_args (arglist, complain) == NULL)
            result = error_mark_node;
          else
-           result = build_over_call (cand, LOOKUP_NORMAL, complain);
+           result = build_over_call_maybe_devirtualize (cand, complain);
        }
       else
        {
@@ -5755,7 +5775,10 @@ convert_like_real (conversion *convs, tree expr, t
        for (i = 0; i < cand->num_convs; ++i)
          cand->convs[i]->user_conv_p = true;
 
-       expr = build_over_call (cand, LOOKUP_NORMAL, complain);
+       if (TREE_CODE (convfn) == FUNCTION_DECL)
+         expr = build_over_call_maybe_devirtualize (cand, complain);
+       else
+         expr = build_over_call (cand, LOOKUP_NORMAL, complain);
 
        /* If this is a constructor or a function returning an aggr type,
           we need to build up a TARGET_EXPR.  */
Index: testsuite/g++.dg/other/final2.C
===================================================================
--- testsuite/g++.dg/other/final2.C     (revision 0)
+++ testsuite/g++.dg/other/final2.C     (revision 0)
@@ -0,0 +1,27 @@
+// PR c++/53186
+// { dg-options "-fdump-tree-original -std=c++11"  }
+
+struct F1
+{
+  virtual void operator()() final;
+  virtual operator int() final;
+  virtual int operator++() final;
+};
+
+struct F2 final
+{
+  virtual void operator()();
+  virtual operator int();
+  virtual int operator++();
+};
+
+void fooF1(F1& a) { a(); int m = a; ++a; }
+void fooF2(F2& a) { a(); int m = a; ++a; }
+
+// { dg-final { scan-tree-dump-times "F1::operator\\(\\)" 1 "original" } }
+// { dg-final { scan-tree-dump-times "F1::operator int" 1 "original" } }
+// { dg-final { scan-tree-dump-times "F1::operator\\+\\+" 1 "original" } }
+// { dg-final { scan-tree-dump-times "F2::operator\\(\\)" 1 "original" } }
+// { dg-final { scan-tree-dump-times "F2::operator int" 1 "original" } }
+// { dg-final { scan-tree-dump-times "F2::operator\\+\\+" 1 "original" } }
+// { dg-final { cleanup-tree-dump "original" } }

Reply via email to