c++: avoid duplicate deprecated warning [PR90451]

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.
This commit is contained in:
Jason Merrill 2022-02-16 14:05:39 -05:00
parent efbb17db52
commit c352ef0ed9
9 changed files with 132 additions and 18 deletions

View 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);

View 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;

View 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

View 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)))

View 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);

View 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;

View 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);

View File

@ -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 }
}

View File

@ -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 }