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. And another thing isn't in a patch, but I'm wondering whether we don't handle it incorrectly. constexpr.c has: /* Check that immediate invocation does not return an expression referencing any immediate function decls. They need to be allowed while parsing immediate functions, but can't leak outside of them. */ if (is_consteval && t != r && (current_function_decl == NULL_TREE || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) as condition for the discovery of embedded immediate FUNCTION_DECLs (or now PTRMEM_CSTs). If I remove the && (current... ..._decl)) then g++.dg/cpp2a/consteval7.C's struct S { int b; int (*c) (); }; consteval S baz () { return { 5, foo }; } consteval int qux () { S s = baz (); return s.b + s.c (); } consteval int quux () { constexpr S s = baz (); return s.b + s.c (); } quux line fails, but based on http://eel.is/c++draft/expr.const#11 I wonder if it shouldn't fail (clang++ -std=c++20 rejects it), and be only accepted without the constexpr keyword before S s. Also wonder about e.g. consteval int foo () { return 42; } consteval int bar () { auto fn1 = foo; // This must be ok constexpr auto fn2 = foo; // Isn't this an error? return fn1 () + fn2 (); } constexpr int baz () { if consteval { auto fn1 = foo; // This must be ok constexpr auto fn2 = foo; // Isn't this an error? return fn1 () + fn2 (); } return 0; } auto a = bar (); static_assert (bar () == 84); static_assert (baz () == 84); (again, clang++ -std=c++20 rejects the fn2 = foo; case, but doesn't implement consteval if, so can't test the other one). For taking address of an immediate function or method if it is taken outside of immediate function context we already have diagnostics about it, but shouldn't the immediate FUNCTION_DECL discovery in cxx_eval_outermost_constant_expression be instead guarded with something like if (is_consteval || in_immediate_context ()) and be done regardless of whether t != r? 2021-10-19 Jakub Jelinek <ja...@redhat.com> PR c++/102753 * cp-tree.h (in_immediate_context): Declare. * call.c (in_immediate_context): New function. (immediate_invocation_p): Use it. (build_over_call): Temporarily set in_consteval_if_p for convert_default_arg calls of immediate invocations. * 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/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. --- gcc/cp/cp-tree.h.jj 2021-10-15 11:58:44.968133548 +0200 +++ gcc/cp/cp-tree.h 2021-10-19 10:40:58.375799274 +0200 @@ -6547,6 +6547,7 @@ extern tree perform_direct_initializatio tsubst_flags_t); extern vec<tree,va_gc> *resolve_args (vec<tree,va_gc>*, 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); --- gcc/cp/call.c.jj 2021-10-15 11:58:44.947133850 +0200 +++ gcc/cp/call.c 2021-10-19 13:04:42.333421774 +0200 @@ -9025,6 +9025,19 @@ build_trivial_dtor_call (tree instance, instance, clobber); } +/* Return true if in an immediate function context. */ + +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,12 +9046,7 @@ immediate_invocation_p (tree fn, int nar { 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. */ @@ -9451,6 +9459,12 @@ build_over_call (struct z_candidate *can } /* Default arguments */ + bool save_in_consteval_if_p = in_consteval_if_p; + /* If the call is immediate function invocation, make sure + taking address of immediate functions is allowed in default + arguments. */ + if (immediate_invocation_p (STRIP_TEMPLATE (fn), nargs)) + in_consteval_if_p = true; for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++) { if (TREE_VALUE (parm) == error_mark_node) @@ -9463,6 +9477,7 @@ build_over_call (struct z_candidate *can return error_mark_node; argarray[j++] = val; } + in_consteval_if_p = save_in_consteval_if_p; /* Ellipsis */ int magic = magic_varargs_p (fn); --- gcc/cp/typeck.c.jj 2021-10-18 11:01:08.635858336 +0200 +++ gcc/cp/typeck.c 2021-10-19 10:42:18.454671723 +0200 @@ -6773,9 +6773,19 @@ cp_build_addr_expr_1 (tree arg, bool str 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 str 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", --- gcc/cp/constexpr.c.jj 2021-10-19 09:24:41.938242276 +0200 +++ gcc/cp/constexpr.c 2021-10-19 12:22:35.583964001 +0200 @@ -7276,6 +7276,10 @@ find_immediate_fndecl (tree *tp, int */* { 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; } --- gcc/testsuite/g++.dg/cpp2a/consteval20.C.jj 2021-10-19 10:29:30.471484783 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval20.C 2021-10-19 10:29:30.471484783 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp2a/consteval21.C.jj 2021-10-19 11:09:18.890838778 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval21.C 2021-10-19 11:57:28.309175141 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp2a/consteval22.C.jj 2021-10-19 11:21:44.271346868 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval22.C 2021-10-19 12:23:31.783173408 +0200 @@ -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" } --- gcc/testsuite/g++.dg/cpp2a/consteval23.C.jj 2021-10-19 12:23:54.235857548 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval23.C 2021-10-19 12:24:33.931299123 +0200 @@ -0,0 +1,12 @@ +// 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); --- gcc/testsuite/g++.dg/cpp23/consteval-if11.C.jj 2021-10-19 11:17:25.964982502 +0200 +++ gcc/testsuite/g++.dg/cpp23/consteval-if11.C 2021-10-19 11:35:38.878602026 +0200 @@ -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); Jakub