[pushed] c++: avoid duplicate deprecated warning [PR90451]

Message ID 20220217212217.2118040-1-jason@redhat.com
State Committed
Commit c352ef0ed90cfc07d494dfec111121bc683e337b
Headers
Series [pushed] c++: avoid duplicate deprecated warning [PR90451] |

Commit Message

Jason Merrill Feb. 17, 2022, 9:22 p.m. UTC
  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.

Tested x86_64-pc-linux-gnu, applying to trunk.

	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                          |  1 +
 gcc/cp/cvt.cc                             |  3 +
 gcc/cp/decl2.cc                           | 23 ++++++++
 gcc/cp/init.cc                            |  5 +-
 gcc/cp/pt.cc                              |  4 ++
 gcc/cp/semantics.cc                       | 22 ++-----
 gcc/cp/typeck.cc                          |  6 ++
 gcc/testsuite/g++.dg/warn/deprecated-14.C | 72 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/warn/deprecated-15.C | 14 +++++
 9 files changed, 132 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/deprecated-14.C
 create mode 100644 gcc/testsuite/g++.dg/warn/deprecated-15.C


base-commit: 1c2b44b52364cb5661095b346de794bc7ff02866
  

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f253b32c3f2..37d462fca6e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index e9803c1be31..53aa41368fe 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -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;
 
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index c6bfcfe631a..2e58419ea51 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -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
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index fcb255f1ac7..545d904c0f9 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -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)))
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 6dda66081bd..d4a40d517d1 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -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);
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0cb17a6a8ab..94c0f183fb1 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -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;
 
@@ -4202,9 +4205,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))
@@ -4236,18 +4236,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
@@ -11256,6 +11244,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;
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 50d04c1b84e..f796337f73c 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -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
index 00000000000..7ad7ec49c7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/deprecated-14.C
@@ -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
index 00000000000..7f25c46a9d7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/deprecated-15.C
@@ -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 }