From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2153) id 550013858C27; Wed, 27 Oct 2021 07:28:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 550013858C27 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jakub Jelinek To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-4730] c++: Diagnose taking address of an immediate member function [PR102753] X-Act-Checkin: gcc X-Git-Author: Jakub Jelinek X-Git-Refname: refs/heads/master X-Git-Oldrev: 3ff5b4edbed4d7b31a88e748e482b01fb5428a8c X-Git-Newrev: 4b2fda8bea3fdcc9421726e5a21e537f745cad0b Message-Id: <20211027072844.550013858C27@sourceware.org> Date: Wed, 27 Oct 2021 07:28:44 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 27 Oct 2021 07:28:44 -0000 https://gcc.gnu.org/g:4b2fda8bea3fdcc9421726e5a21e537f745cad0b commit r12-4730-g4b2fda8bea3fdcc9421726e5a21e537f745cad0b Author: Jakub Jelinek Date: Wed Oct 27 09:03:28 2021 +0200 c++: Diagnose taking address of an immediate member function [PR102753] The consteval20.C testcase ICEs, because while we have in cp_build_addr_expr_1 diagnostics for taking address of an immediate function (and as an exception deal with build_address from immediate invocation), I forgot to diagnose taking address of a member function which is done in a different place. I hope (s.*&S::foo) () is not an immediate invocation like (*&foo) () is not, so this patch just diagnoses taking address of a member function when not in immediate context. On Mon, Oct 18, 2021 at 12:42:00PM -0400, Jason Merrill wrote: > > --- gcc/cp/typeck.c.jj 2021-10-05 09:53:55.382734051 +0200 > > +++ gcc/cp/typeck.c 2021-10-15 19:28:38.034213437 +0200 > > @@ -6773,9 +6773,21 @@ cp_build_addr_expr_1 (tree arg, bool str > > return error_mark_node; > > } > > + if (TREE_CODE (t) == FUNCTION_DECL > > + && DECL_IMMEDIATE_FUNCTION_P (t) > > + && cp_unevaluated_operand == 0 > > + && (current_function_decl == NULL_TREE > > + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) > > This doesn't cover some of the other cases of immediate context; we should > probably factor most of immediate_invocation_p out into a function called > something like in_immediate_context and use it here, and in several other > places as well. You're right, I've done that for the two spots in cp_build_addr_expr_1 and added testsuite coverage for where it changed behavior. While doing that I've discovered further issues. One is that we weren't diagnosing PMFs referring to immediate methods returned from immediate functions (either directly or embedded in aggregates). I'm not sure if it can only appear as PTRMEM_CST which I've handled (cp_walk_subtree only walks the type and not the PTRMEM_CST_MEMBER) or something else. Another issue is that while default arg in immediate function containing &immediate_fn works properly, if it is immediate_fn instead, we were incorrectly rejecting it. I've handled this in build_over_call, though with this usage in_consteval_if_p is slightly misnamed, it stands for in consteval if or some other reason why we are currently in immediate function context. Though, that flag alone can't be all the reasons for being in immediate function contexts, as I've tried the other reasons can't be handled in such a bool and need to be tested too. 2021-10-27 Jakub Jelinek PR c++/102753 * cp-tree.h (saved_scope): Document that consteval_if_p member is also set while processing immediate invocation. (in_immediate_context): Declare. * call.c (in_immediate_context): New function. (immediate_invocation_p): Use it. (struct in_consteval_if_p_temp_override): New class. (build_over_call): Temporarily set in_consteval_if_p for processing immediate invocation arguments. * typeck.c (cp_build_addr_expr_1): Diagnose taking address of an immediate method. Use t instead of TREE_OPERAND (arg, 1). Use in_immediate_context function. * constexpr.c (find_immediate_fndecl): Handle PTRMEM_CST which refers to immediate function decl. * g++.dg/cpp2a/consteval13.C: Don't expect errors. * g++.dg/cpp2a/consteval20.C: New test. * g++.dg/cpp2a/consteval21.C: New test. * g++.dg/cpp2a/consteval22.C: New test. * g++.dg/cpp2a/consteval23.C: New test. * g++.dg/cpp23/consteval-if11.C: New test. Diff: --- gcc/cp/call.c | 40 ++++++++++++++++++++++++----- gcc/cp/constexpr.c | 4 +++ gcc/cp/cp-tree.h | 4 ++- gcc/cp/typeck.c | 16 +++++++++--- gcc/testsuite/g++.dg/cpp23/consteval-if11.C | 27 +++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval13.C | 4 +-- gcc/testsuite/g++.dg/cpp2a/consteval20.C | 24 +++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval21.C | 35 +++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval22.C | 34 ++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval23.C | 13 ++++++++++ 10 files changed, 188 insertions(+), 13 deletions(-) diff --git a/gcc/cp/call.c b/gcc/cp/call.c index c5601d96ab8..20e66c6c63f 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -9025,6 +9025,20 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref) instance, clobber); } +/* Return true if in an immediate function context, or an unevaluated operand, + or a subexpression of an immediate invocation. */ + +bool +in_immediate_context () +{ + return (cp_unevaluated_operand != 0 + || (current_function_decl != NULL_TREE + && DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) + || (current_binding_level->kind == sk_function_parms + && current_binding_level->immediate_fn_ctx_p) + || in_consteval_if_p); +} + /* Return true if a call to FN with number of arguments NARGS is an immediate invocation. */ @@ -9033,18 +9047,25 @@ immediate_invocation_p (tree fn, int nargs) { return (TREE_CODE (fn) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (fn) - && cp_unevaluated_operand == 0 - && (current_function_decl == NULL_TREE - || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) - && (current_binding_level->kind != sk_function_parms - || !current_binding_level->immediate_fn_ctx_p) - && !in_consteval_if_p + && !in_immediate_context () /* As an exception, we defer std::source_location::current () invocations until genericization because LWG3396 mandates special behavior for it. */ && (nargs > 1 || !source_location_current_p (fn))); } +/* temp_override for in_consteval_if_p, which can't use make_temp_override + because it is a bitfield. */ + +struct in_consteval_if_p_temp_override { + bool save_in_consteval_if_p; + in_consteval_if_p_temp_override () + : save_in_consteval_if_p (in_consteval_if_p) {} + void reset () { in_consteval_if_p = save_in_consteval_if_p; } + ~in_consteval_if_p_temp_override () + { reset (); } +}; + /* Subroutine of the various build_*_call functions. Overload resolution has chosen a winning candidate CAND; build up a CALL_EXPR accordingly. ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a @@ -9254,6 +9275,12 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) nargs = parmlen; argarray = XALLOCAVEC (tree, nargs); + in_consteval_if_p_temp_override icip; + /* If the call is immediate function invocation, make sure + taking address of immediate functions is allowed in its arguments. */ + if (immediate_invocation_p (STRIP_TEMPLATE (fn), nargs)) + in_consteval_if_p = true; + /* The implicit parameters to a constructor are not considered by overload resolution, and must be of the proper type. */ if (DECL_CONSTRUCTOR_P (fn)) @@ -9498,6 +9525,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) gcc_assert (j <= nargs); nargs = j; + icip.reset (); /* Avoid performing argument transformation if warnings are disabled. When tf_warning is set and at least one of the warnings is active diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 6f83d303cdd..daa6358d08f 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -7276,6 +7276,10 @@ find_immediate_fndecl (tree *tp, int */*walk_subtrees*/, void */*data*/) { if (TREE_CODE (*tp) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (*tp)) return *tp; + if (TREE_CODE (*tp) == PTRMEM_CST + && TREE_CODE (PTRMEM_CST_MEMBER (*tp)) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (*tp))) + return PTRMEM_CST_MEMBER (*tp); return NULL_TREE; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5248ecd8fe7..f387b5036d2 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1825,7 +1825,8 @@ struct GTY(()) saved_scope { if-statement. */ BOOL_BITFIELD discarded_stmt : 1; /* Nonzero if we are parsing or instantiating the compound-statement - of consteval if statement. */ + of consteval if statement. Also set while processing an immediate + invocation. */ BOOL_BITFIELD consteval_if_p : 1; int unevaluated_operand; @@ -6547,6 +6548,7 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool, tsubst_flags_t); extern vec *resolve_args (vec*, tsubst_flags_t); extern tree in_charge_arg_for_name (tree); +extern bool in_immediate_context (); extern tree build_cxx_call (tree, int, tree *, tsubst_flags_t, tree = NULL_TREE); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index ab0f9da2552..d5f50001dc0 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6773,9 +6773,19 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) return error_mark_node; } + if (TREE_CODE (t) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (t) + && !in_immediate_context ()) + { + if (complain & tf_error) + error_at (loc, "taking address of an immediate function %qD", + t); + return error_mark_node; + } + type = build_ptrmem_type (context_for_name_lookup (t), TREE_TYPE (t)); - t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1)); + t = make_ptrmem_cst (type, t); return t; } @@ -6800,9 +6810,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) tree stripped_arg = tree_strip_any_location_wrapper (arg); if (TREE_CODE (stripped_arg) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (stripped_arg) - && cp_unevaluated_operand == 0 - && (current_function_decl == NULL_TREE - || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) + && !in_immediate_context ()) { if (complain & tf_error) error_at (loc, "taking address of an immediate function %qD", diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if11.C b/gcc/testsuite/g++.dg/cpp23/consteval-if11.C new file mode 100644 index 00000000000..a22736cc006 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if11.C @@ -0,0 +1,27 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +struct S { + constexpr S () : s (0) {} + consteval int foo () { return 1; } + virtual consteval int bar () { return 2; } + int s; +}; + +consteval int foo () { return 42; } + +constexpr int +bar () +{ + if consteval { // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + int (*fn1) () = foo; + int (S::*fn2) () = &S::foo; + int (S::*fn3) () = &S::bar; + S s; + return fn1 () + (s.*fn2) () + (s.*fn3) (); + } + return 0; +} + +static_assert (bar () == 45); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval13.C b/gcc/testsuite/g++.dg/cpp2a/consteval13.C index a2e1750b3ca..09ea6b70b6b 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval13.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval13.C @@ -10,8 +10,8 @@ void foo () { auto qux = [] (fnptr a = quux ()) consteval { return a (); }; - constexpr auto c = qux (baz); // { dg-error "28:taking address of an immediate function" } - constexpr auto d = qux (bar); // { dg-error "28:taking address of an immediate function" } + constexpr auto c = qux (baz); + constexpr auto d = qux (bar); static_assert (c == 1); static_assert (d == 42); } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval20.C b/gcc/testsuite/g++.dg/cpp2a/consteval20.C new file mode 100644 index 00000000000..2c359637c61 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval20.C @@ -0,0 +1,24 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +struct S { + consteval int foo () const { return 42; } +}; + +constexpr S s; + +int +bar () +{ + return (s.*&S::foo) (); // { dg-error "taking address of an immediate function" } +} + +constexpr auto a = &S::foo; // { dg-error "taking address of an immediate function" } + +consteval int +baz () +{ + return (s.*&S::foo) (); +} + +static_assert (baz () == 42); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval21.C b/gcc/testsuite/g++.dg/cpp2a/consteval21.C new file mode 100644 index 00000000000..06ec705c75b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval21.C @@ -0,0 +1,35 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +struct S { + constexpr S () : s (0) {} + consteval int foo () { return 1; } + virtual consteval int bar () { return 2; } + int s; +}; + +consteval int foo () { return 42; } + +consteval int +bar (int (*fn) () = &foo) +{ + return fn (); +} + +consteval int +baz (int (S::*fn) () = &S::foo) +{ + S s; + return (s.*fn) (); +} + +consteval int +qux (int (S::*fn) () = &S::bar) +{ + S s; + return (s.*fn) (); +} + +static_assert (bar () == 42); +static_assert (baz () == 1); +static_assert (qux () == 2); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval22.C b/gcc/testsuite/g++.dg/cpp2a/consteval22.C new file mode 100644 index 00000000000..5c3637189dc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval22.C @@ -0,0 +1,34 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +struct S { + constexpr S () : s (0) {} + consteval int foo () { return 1; } + virtual consteval int bar () { return 2; } + int s; +}; +typedef int (S::*P) (); + +consteval P +foo () +{ + return &S::foo; +} + +consteval P +bar () +{ + return &S::bar; +} + +consteval int +baz () +{ + S s; + return (s.*(foo ())) () + (s.*(bar ())) (); +} + +static_assert (baz () == 3); + +constexpr P a = foo (); // { dg-error "immediate evaluation returns address of immediate function" } +constexpr P b = bar (); // { dg-error "immediate evaluation returns address of immediate function" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval23.C b/gcc/testsuite/g++.dg/cpp2a/consteval23.C new file mode 100644 index 00000000000..4c7e844c450 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval23.C @@ -0,0 +1,13 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } + +consteval int +bar (int (*fn) () = foo) +{ + return fn (); +} + +static_assert (bar () == 42); +static_assert (bar (foo) == 42);