c++: avoid duplicate deprecated warning [PR90451]
authorJason Merrill <jason@redhat.com>
Wed, 16 Feb 2022 19:05:39 +0000 (14:05 -0500)
committerJason Merrill <jason@redhat.com>
Thu, 17 Feb 2022 21:22:27 +0000 (16:22 -0500)
We were getting the deprecated warning twice for the same call because we
called mark_used first in finish_qualified_id_expr and then again in
build_over_call.  Let's not call it the first time; C++17 clarified that a
function is used only when it is selected from an overload set, which
happens later.

Then I had to add a few more uses in places that don't do anything further
with the expression (convert_to_void, finish_decltype_type), and places that
use the expression more unusually (cp_build_addr_expr_1,
convert_nontype_argument).  The new mark_single_function is mostly so
that I only have to put the comment in one place.

PR c++/90451

gcc/cp/ChangeLog:

* decl2.cc (mark_single_function): New.
* cp-tree.h: Declare it.
* typeck.cc (cp_build_addr_expr_1): mark_used when making a PMF.
* semantics.cc (finish_qualified_id_expr): Not here.
(finish_id_expression_1): Or here.
(finish_decltype_type): Call mark_single_function.
* cvt.cc (convert_to_void): And here.
* pt.cc (convert_nontype_argument): And here.
* init.cc (build_offset_ref): Adjust assert.

gcc/testsuite/ChangeLog:

* g++.dg/warn/deprecated-14.C: New test.
* g++.dg/warn/deprecated-15.C: New test.

gcc/cp/cp-tree.h
gcc/cp/cvt.cc
gcc/cp/decl2.cc
gcc/cp/init.cc
gcc/cp/pt.cc
gcc/cp/semantics.cc
gcc/cp/typeck.cc
gcc/testsuite/g++.dg/warn/deprecated-14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/deprecated-15.C [new file with mode: 0644]

index f253b32..37d462f 100644 (file)
@@ -6930,6 +6930,7 @@ extern void no_linkage_error                      (tree);
 extern void check_default_args                 (tree);
 extern bool mark_used                          (tree);
 extern bool mark_used                          (tree, tsubst_flags_t);
+extern bool mark_single_function               (tree, tsubst_flags_t);
 extern void finish_static_data_member_decl     (tree, tree, bool, tree, int);
 extern tree cp_build_parm_decl                 (tree, tree, tree);
 extern void copy_linkage                       (tree, tree);
index e9803c1..53aa413 100644 (file)
@@ -1482,6 +1482,9 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
     default:;
     }
   expr = resolve_nondeduced_context (expr, complain);
+  if (!mark_single_function (expr, complain))
+    return error_mark_node;
+
   {
     tree probe = expr;
 
index c6bfcfe..2e58419 100644 (file)
@@ -5718,6 +5718,29 @@ decl_dependent_p (tree decl)
   return false;
 }
 
+/* [basic.def.odr] A function is named [and therefore odr-used] by an
+   expression or conversion if it is the selected member of an overload set in
+   an overload resolution performed as part of forming that expression or
+   conversion, unless it is a pure virtual function and either the expression
+   is not an id-expression naming the function with an explicitly qualified
+   name or the expression forms a pointer to member.
+
+   Mostly, we call mark_used in places that actually do something with a
+   function, like build_over_call.  But in a few places we end up with a
+   non-overloaded FUNCTION_DECL that we aren't going to do any more with, like
+   convert_to_void.  resolve_nondeduced_context is called in those places,
+   but it's also called in too many other places.  */
+
+bool
+mark_single_function (tree expr, tsubst_flags_t complain)
+{
+  if (is_overloaded_fn (expr) == 1
+      && !mark_used (expr, complain)
+      && (complain & tf_error))
+    return false;
+  return true;
+}
+
 /* Mark DECL (either a _DECL or a BASELINK) as "used" in the program.
    If DECL is a specialization or implicitly declared class member,
    generate the actual definition.  Return false if something goes
index fcb255f..545d904 100644 (file)
@@ -2362,8 +2362,9 @@ build_offset_ref (tree type, tree member, bool address_p,
     return error_mark_node;
 
   gcc_assert (DECL_P (member) || BASELINK_P (member));
-  /* Callers should call mark_used before this point.  */
-  gcc_assert (!DECL_P (member) || TREE_USED (member));
+  /* Callers should call mark_used before this point, except for functions.  */
+  gcc_assert (!DECL_P (member) || TREE_USED (member)
+             || TREE_CODE (member) == FUNCTION_DECL);
 
   type = TYPE_MAIN_VARIANT (type);
   if (!COMPLETE_OR_OPEN_TYPE_P (complete_type (type)))
index 6dda660..d4a40d5 100644 (file)
@@ -7382,6 +7382,10 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
      for examples.  */
   if (TYPE_REF_OBJ_P (type) || TYPE_REFFN_P (type))
     {
+      /* Check this before we strip *& to avoid redundancy.  */
+      if (!mark_single_function (expr, complain))
+       return error_mark_node;
+
       tree probe_type, probe = expr;
       if (REFERENCE_REF_P (probe))
        probe = TREE_OPERAND (probe, 0);
index 114baa4..a2c0eb0 100644 (file)
@@ -2319,7 +2319,10 @@ finish_qualified_id_expr (tree qualifying_class,
   if (error_operand_p (expr))
     return error_mark_node;
 
-  if ((DECL_P (expr) || BASELINK_P (expr))
+  if (DECL_P (expr)
+      /* Functions are marked after overload resolution; avoid redundant
+        warnings.  */
+      && TREE_CODE (expr) != FUNCTION_DECL
       && !mark_used (expr, complain))
     return error_mark_node;
 
@@ -4198,9 +4201,6 @@ finish_id_expression_1 (tree id_expression,
          decl = (adjust_result_of_qualified_name_lookup
                  (decl, scope, current_nonlambda_class_type()));
 
-         if (TREE_CODE (decl) == FUNCTION_DECL)
-           mark_used (decl);
-
          cp_warn_deprecated_use_scopes (scope);
 
          if (TYPE_P (scope))
@@ -4232,18 +4232,6 @@ finish_id_expression_1 (tree id_expression,
          tree first_fn = get_first_fn (decl);
          first_fn = STRIP_TEMPLATE (first_fn);
 
-         /* [basic.def.odr]: "A function whose name appears as a
-            potentially-evaluated expression is odr-used if it is the unique
-            lookup result".
-
-            But only mark it if it's a complete postfix-expression; in a call,
-            ADL might select a different function, and we'll call mark_used in
-            build_over_call.  */
-         if (done
-             && !really_overloaded_fn (decl)
-             && !mark_used (first_fn))
-           return error_mark_node;
-
          if (!template_arg_p
              && (TREE_CODE (first_fn) == USING_DECL
                  || (TREE_CODE (first_fn) == FUNCTION_DECL
@@ -11252,6 +11240,8 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
   /* The type denoted by decltype(e) is defined as follows:  */
 
   expr = resolve_nondeduced_context (expr, complain);
+  if (!mark_single_function (expr, complain))
+    return error_mark_node;
 
   if (invalid_nonstatic_memfn_p (input_location, expr, complain))
     return error_mark_node;
index 50d04c1..f796337 100644 (file)
@@ -6854,6 +6854,12 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
            return error_mark_node;
          }
 
+       /* Forming a pointer-to-member is a use of non-pure-virtual fns.  */
+       if (TREE_CODE (t) == FUNCTION_DECL
+           && !DECL_PURE_VIRTUAL_P (t)
+           && !mark_used (t, complain) && !(complain & tf_error))
+         return error_mark_node;
+
        type = build_ptrmem_type (context_for_name_lookup (t),
                                  TREE_TYPE (t));
        t = make_ptrmem_cst (type, t);
diff --git a/gcc/testsuite/g++.dg/warn/deprecated-14.C b/gcc/testsuite/g++.dg/warn/deprecated-14.C
new file mode 100644 (file)
index 0000000..7ad7ec4
--- /dev/null
@@ -0,0 +1,72 @@
+// PR c++/90451
+// { dg-do compile { target c++11 } }
+
+struct myclass{
+  [[deprecated("deprecated-static1")]] static void stat1() { }
+  [[deprecated("deprecated-static2")]] static void stat2() { }
+  [[deprecated("deprecated-static3")]] static void stat3() { }
+  [[deprecated("deprecated-static4")]] static void stat4() { }
+
+  [[deprecated("deprecated-non1")]] void non1() { }
+  [[deprecated("deprecated-non2")]] void non2() { }
+};
+
+[[deprecated("deprecated-global1")]] void fn1();
+[[deprecated("deprecated-global2")]] void fn2();
+[[deprecated("deprecated-global3")]] void fn3();
+
+[[deprecated("deprecated-global4")]] void fn4();
+[[deprecated("deprecated-global5")]] void fn5();
+[[deprecated("deprecated-global6")]] void fn6();
+[[deprecated("deprecated-global7")]] void fn7();
+[[deprecated("deprecated-global8")]] void fn8();
+
+namespace N
+{
+  [[deprecated("deprecated-ns1")]] void fn1();
+  [[deprecated("deprecated-ns2")]] void fn2();
+  [[deprecated("deprecated-ns3")]] void fn3();
+}
+
+int main()
+{
+  myclass::stat1(); // { dg-bogus "deprecated-static1.*deprecated-static1" }
+  // { dg-warning "deprecated-static1" "" { target *-*-* } .-1 }
+  &myclass::stat2; // { dg-bogus "deprecated-static2.*deprecated-static2" }
+  // { dg-warning "deprecated-static2" "" { target *-*-* } .-1 }
+  auto x = myclass::stat3; // { dg-bogus "deprecated-static3.*deprecated-static3" }
+  // { dg-warning "deprecated-static3" "" { target *-*-* } .-1 }
+  (void) myclass::stat4; // { dg-bogus "deprecated-static4.*deprecated-static4" }
+  // { dg-warning "deprecated-static4" "" { target *-*-* } .-1 }
+
+  myclass m;
+  m.myclass::non1(); // { dg-bogus "deprecated-non1.*deprecated-non1" }
+  // { dg-warning "deprecated-non1" "" { target *-*-* } .-1 }
+  &myclass::non2; // { dg-bogus "deprecated-non2.*deprecated-non2" }
+  // { dg-warning "deprecated-non2" "" { target *-*-* } .-1 }
+
+  fn1(); // { dg-bogus "deprecated-global1.*deprecated-global1" }
+  // { dg-warning "deprecated-global1" "" { target *-*-* } .-1 }
+  &fn2; // { dg-bogus "deprecated-global2.*deprecated-global2" }
+  // { dg-warning "deprecated-global2" "" { target *-*-* } .-1 }
+  auto xg = fn3; // { dg-bogus "deprecated-global2.*deprecated-global3" }
+  // { dg-warning "deprecated-global3" "" { target *-*-* } .-1 }
+  (void) fn7; // { dg-bogus "deprecated-global7.*deprecated-global7" }
+  // { dg-warning "deprecated-global7" "" { target *-*-* } .-1 }
+
+  ::fn4(); // { dg-bogus "deprecated-global4.*deprecated-global4" }
+  // { dg-warning "deprecated-global4" "" { target *-*-* } .-1 }
+  &::fn5; // { dg-bogus "deprecated-global5.*deprecated-global5" }
+  // { dg-warning "deprecated-global5" "" { target *-*-* } .-1 }
+  auto xgs = ::fn6; // { dg-bogus "deprecated-global2.*deprecated-global6" }
+  // { dg-warning "deprecated-global6" "" { target *-*-* } .-1 }
+  (void) ::fn8; // { dg-bogus "deprecated-global8.*deprecated-global8" }
+  // { dg-warning "deprecated-global8" "" { target *-*-* } .-1 }
+
+  N::fn1(); // { dg-bogus "deprecated-ns1.*deprecated-ns1" }
+  // { dg-warning "deprecated-ns1" "" { target *-*-* } .-1 }
+  &N::fn2; // { dg-bogus "deprecated-ns2.*deprecated-ns2" }
+  // { dg-warning "deprecated-ns2" "" { target *-*-* } .-1 }
+  auto xgn = N::fn3; // { dg-bogus "deprecated-ns2.*deprecated-ns3" }
+  // { dg-warning "deprecated-ns3" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/g++.dg/warn/deprecated-15.C b/gcc/testsuite/g++.dg/warn/deprecated-15.C
new file mode 100644 (file)
index 0000000..7f25c46
--- /dev/null
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+
+using vfn_t = void();
+
+template <vfn_t *T> struct A { };
+template <vfn_t& T> struct B { };
+
+[[deprecated("deprecated-global1")]] void fn1();
+[[deprecated("deprecated-global2")]] void fn2();
+
+A<fn1> a; // { dg-bogus "deprecated-global1.*deprecated-global1" }
+// { dg-warning "deprecated-global1" "" { target *-*-* } .-1 }
+B<fn2> b; // { dg-bogus "deprecated-global2.*deprecated-global2" }
+// { dg-warning "deprecated-global2" "" { target *-*-* } .-1 }