From c352ef0ed90cfc07d494dfec111121bc683e337b Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 16 Feb 2022 14:05:39 -0500 Subject: [PATCH] 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. --- 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 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f253b32..37d462f 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 e9803c1..53aa413 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 c6bfcfe..2e58419 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 fcb255f..545d904 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 6dda660..d4a40d5 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 114baa4..a2c0eb0 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; @@ -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; diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 50d04c1..f796337 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 0000000..7ad7ec4 --- /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 0000000..7f25c46 --- /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 struct A { }; +template struct B { }; + +[[deprecated("deprecated-global1")]] void fn1(); +[[deprecated("deprecated-global2")]] void fn2(); + +A a; // { dg-bogus "deprecated-global1.*deprecated-global1" } +// { dg-warning "deprecated-global1" "" { target *-*-* } .-1 } +B b; // { dg-bogus "deprecated-global2.*deprecated-global2" } +// { dg-warning "deprecated-global2" "" { target *-*-* } .-1 } -- 2.7.4