* [PATCH] c++: Move consteval folding to cp_fold_r @ 2023-09-01 17:23 Marek Polacek 2023-09-01 17:36 ` Marek Polacek 2023-09-05 14:52 ` Jason Merrill 0 siblings, 2 replies; 19+ messages in thread From: Marek Polacek @ 2023-09-01 17:23 UTC (permalink / raw) To: Jason Merrill, GCC Patches Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process as much code as we did before: uninstantiated templates and things like "false ? foo () : 1". You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (in_immediate_context): No longer static. (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (cxx_eval_call_expression): Use mce_true for immediate_invocation_p. * cp-gimplify.cc (cp_fold_r): Expand immediate invocations. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (immediate_invocation_p): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. --- gcc/cp/call.cc | 42 +++---------------- gcc/cp/constexpr.cc | 5 +++ gcc/cp/cp-gimplify.cc | 14 ++++++- gcc/cp/cp-tree.h | 6 +++ gcc/cp/tree.cc | 23 +--------- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 2 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 ++++ gcc/testsuite/g++.dg/cpp2a/consteval11.C | 37 ++++++++-------- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 ++ gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 +++++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 2 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 13 files changed, 100 insertions(+), 81 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 40d9fdc0516..abdbc8fff8c 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -9763,7 +9763,7 @@ in_immediate_context () /* Return true if a call to FN with number of arguments NARGS is an immediate invocation. */ -static bool +bool immediate_invocation_p (tree fn) { return (TREE_CODE (fn) == FUNCTION_DECL @@ -10471,6 +10471,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10488,41 +10492,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 8bd5c4a47f8..af4f98b1fe1 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (immediate_invocation_p (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..29132aad158 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1058,9 +1058,21 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (!in_immediate_context ()) + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + *stmt_p = stmt = cxx_constant_value (stmt); + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) + && !in_immediate_context ()) { error_at (EXPR_LOCATION (stmt), "taking address of an immediate function %qD", diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..8bd794c91fb 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6713,6 +6718,7 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool, 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 bool immediate_invocation_p (tree); extern tree build_cxx_call (tree, int, tree *, tsubst_flags_t, tree = NULL_TREE); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..7dfb6de2da3 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int*, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..9fa95295c43 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -65,7 +65,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..9fd32dcab7b 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -5,25 +5,25 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } -const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } -int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; void foo () { constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } - constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } - const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } - int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } - h += 0 ? bar (8) : 1; // { dg-message "in 'constexpr' expansion of" } + int h = 0 ? bar (7) : 1; + h += 0 ? bar (8) : 1; if (0) bar (9); // { dg-message "in 'constexpr' expansion of" } else @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..230a6e9951c 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -18,7 +18,7 @@ void qux () template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 09f27ba86e3..06f1d96d9b7 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -14,6 +14,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: 419c423d3aeca754e47e1ce1bf707735603a90a3 -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] c++: Move consteval folding to cp_fold_r 2023-09-01 17:23 [PATCH] c++: Move consteval folding to cp_fold_r Marek Polacek @ 2023-09-01 17:36 ` Marek Polacek 2023-09-05 14:52 ` Jason Merrill 1 sibling, 0 replies; 19+ messages in thread From: Marek Polacek @ 2023-09-01 17:36 UTC (permalink / raw) To: Jason Merrill, GCC Patches On Fri, Sep 01, 2023 at 01:23:48PM -0400, Marek Polacek via Gcc-patches wrote: > --- a/gcc/cp/cp-gimplify.cc > +++ b/gcc/cp/cp-gimplify.cc [...] > case ADDR_EXPR: > if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL > - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) > + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) > + && !in_immediate_context ()) This hunk isn't actually necessary. I'm happy to drop it. Or add the in_immediate_context check into case PTRMEM_CST too. Marek ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] c++: Move consteval folding to cp_fold_r 2023-09-01 17:23 [PATCH] c++: Move consteval folding to cp_fold_r Marek Polacek 2023-09-01 17:36 ` Marek Polacek @ 2023-09-05 14:52 ` Jason Merrill 2023-09-05 19:59 ` Marek Polacek 1 sibling, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-05 14:52 UTC (permalink / raw) To: Marek Polacek, GCC Patches On 9/1/23 13:23, Marek Polacek wrote: > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > > In the review of P2564: > <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> > it turned out that in order to correctly handle an example in the paper, > we should stop doing immediate evaluation in build_over_call and > bot_replace, and instead do it in cp_fold_r. This patch does that. > > Another benefit is that this is a pretty significant simplification, at > least in my opinion. Also, this fixes the c++/110997 ICE (but the test > doesn't compile yet). > > The main drawback seems to be that cp_fold_r doesn't process as much > code as we did before: uninstantiated templates That's acceptable, it's an optional diagnostic. > and things like "false ? foo () : 1". This is a problem. Maybe we want cp_fold_r to recurse into the arms of a COND_EXPR before folding them away? Maybe only if we know we've seen an immediate function? > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index 8bd5c4a47f8..af4f98b1fe1 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); > unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; > > + /* Make sure we fold std::is_constant_evaluated to true in an > + immediate function. */ > + if (immediate_invocation_p (fun)) I think this should just check DECL_IMMEDIATE_FUNCTION_P, the context doesn't matter. > + call_ctx.manifestly_const_eval = mce_true; > + > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc > index 206e791fcfd..29132aad158 100644 > --- a/gcc/cp/cp-gimplify.cc > +++ b/gcc/cp/cp-gimplify.cc > @@ -1058,9 +1058,21 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) > } > break; > > + /* Expand immediate invocations. */ > + case CALL_EXPR: > + case AGGR_INIT_EXPR: > + if (!in_immediate_context ()) As you mentioned in your followup, we shouldn't need to check this because we don't call cp_fold_r in immediate context. > + if (tree fn = cp_get_callee (stmt)) > + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) > + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > + *stmt_p = stmt = cxx_constant_value (stmt); > + break; > + > case ADDR_EXPR: > if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL > - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) > + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) > + && !in_immediate_context ()) Likewise. > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index 799183dc646..7dfb6de2da3 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) > variables. */ > > static tree > -bot_replace (tree* t, int* walk_subtrees, void* data_) > +bot_replace (tree* t, int*, void* data_) Generally we keep the parameter name as a comment like int */*walk_subtrees*/ > diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C > index d1845da9e58..9fa95295c43 100644 > --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C > +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C > @@ -65,7 +65,7 @@ qux (int x) > int r = 0; > if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } > { > - r += foo (x); // { dg-error "'x' is not a constant expression" } > + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } This whole function should have a comment that these errors are not required because qux is never instantiated. > diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C > index 2f68ec0f892..9fd32dcab7b 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C > +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C > @@ -5,25 +5,25 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n > > constexpr int a = bar (1); > constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } > -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } > +constexpr int c = 0 ? bar (3) : 1; As discussed above, we need to keep this diagnostic and the others like it. Let's also add a test with the template <typename T, typename F> constexpr bool is_not(T t, F f) { return not f(t); } consteval bool is_even(int i) { return i % 2 == 0; } static_assert(is_not(5, is_even)); // ok example from the paper. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] c++: Move consteval folding to cp_fold_r 2023-09-05 14:52 ` Jason Merrill @ 2023-09-05 19:59 ` Marek Polacek 2023-09-05 20:36 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-05 19:59 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Tue, Sep 05, 2023 at 10:52:04AM -0400, Jason Merrill wrote: > On 9/1/23 13:23, Marek Polacek wrote: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > > > In the review of P2564: > > <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> > > it turned out that in order to correctly handle an example in the paper, > > we should stop doing immediate evaluation in build_over_call and > > bot_replace, and instead do it in cp_fold_r. This patch does that. > > > > Another benefit is that this is a pretty significant simplification, at > > least in my opinion. Also, this fixes the c++/110997 ICE (but the test > > doesn't compile yet). > > > > The main drawback seems to be that cp_fold_r doesn't process as much > > code as we did before: uninstantiated templates > > That's acceptable, it's an optional diagnostic. > > > and things like "false ? foo () : 1". > > This is a problem. Maybe we want cp_fold_r to recurse into the arms of a > COND_EXPR before folding them away? Maybe only if we know we've seen an > immediate function? Unfortunately we had already thrown the dead branch away when we got to cp_fold_r. I wonder if we have to adjust cxx_eval_conditional_expression to call cp_fold_r on the dead branch too, perhaps with a new ff_ flag to skip the whole second switch in cp_fold_r? But then it's possible that the in_immediate_context checks have to stay. [ I'll address the rest later. ] Marek ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] c++: Move consteval folding to cp_fold_r 2023-09-05 19:59 ` Marek Polacek @ 2023-09-05 20:36 ` Jason Merrill 2023-09-07 15:23 ` [PATCH v2] " Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-05 20:36 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/5/23 15:59, Marek Polacek wrote: > On Tue, Sep 05, 2023 at 10:52:04AM -0400, Jason Merrill wrote: >> On 9/1/23 13:23, Marek Polacek wrote: >>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? >>> >>> -- >8 -- >>> >>> In the review of P2564: >>> <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> >>> it turned out that in order to correctly handle an example in the paper, >>> we should stop doing immediate evaluation in build_over_call and >>> bot_replace, and instead do it in cp_fold_r. This patch does that. >>> >>> Another benefit is that this is a pretty significant simplification, at >>> least in my opinion. Also, this fixes the c++/110997 ICE (but the test >>> doesn't compile yet). >>> >>> The main drawback seems to be that cp_fold_r doesn't process as much >>> code as we did before: uninstantiated templates >> >> That's acceptable, it's an optional diagnostic. >> >>> and things like "false ? foo () : 1". >> >> This is a problem. Maybe we want cp_fold_r to recurse into the arms of a >> COND_EXPR before folding them away? Maybe only if we know we've seen an >> immediate function? > > Unfortunately we had already thrown the dead branch away when we got to > cp_fold_r. I wonder if we have to adjust cxx_eval_conditional_expression > to call cp_fold_r on the dead branch too, Hmm, I guess so. > perhaps with a new ff_ flag to skip the whole second switch in cp_fold_r? Or factor out the immediate function handling to a separate walk function that cp_fold_r also calls? > But then it's possible that the in_immediate_context checks have to stay. We can just not do the walk in immediate (or mce_true) context, like we currently avoid calling cp_fold_function. For mce_unknown I guess we'd want to set *non_constant_p instead of giving an error. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v2] c++: Move consteval folding to cp_fold_r 2023-09-05 20:36 ` Jason Merrill @ 2023-09-07 15:23 ` Marek Polacek 2023-09-07 18:32 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-07 15:23 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Tue, Sep 05, 2023 at 04:36:34PM -0400, Jason Merrill wrote: > On 9/5/23 15:59, Marek Polacek wrote: > > On Tue, Sep 05, 2023 at 10:52:04AM -0400, Jason Merrill wrote: > > > On 9/1/23 13:23, Marek Polacek wrote: > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > > > > > -- >8 -- > > > > > > > > In the review of P2564: > > > > <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> > > > > it turned out that in order to correctly handle an example in the paper, > > > > we should stop doing immediate evaluation in build_over_call and > > > > bot_replace, and instead do it in cp_fold_r. This patch does that. > > > > > > > > Another benefit is that this is a pretty significant simplification, at > > > > least in my opinion. Also, this fixes the c++/110997 ICE (but the test > > > > doesn't compile yet). > > > > > > > > The main drawback seems to be that cp_fold_r doesn't process as much > > > > code as we did before: uninstantiated templates > > > > > > That's acceptable, it's an optional diagnostic. > > > > > > > and things like "false ? foo () : 1". > > > > > > This is a problem. Maybe we want cp_fold_r to recurse into the arms of a > > > COND_EXPR before folding them away? Maybe only if we know we've seen an > > > immediate function? > > > > Unfortunately we had already thrown the dead branch away when we got to > > cp_fold_r. I wonder if we have to adjust cxx_eval_conditional_expression > > to call cp_fold_r on the dead branch too, > > Hmm, I guess so. > > > perhaps with a new ff_ flag to skip the whole second switch in cp_fold_r? > > Or factor out the immediate function handling to a separate walk function > that cp_fold_r also calls? I did that. > > But then it's possible that the in_immediate_context checks have to stay. > > We can just not do the walk in immediate (or mce_true) context, like we > currently avoid calling cp_fold_function. Right. Unfortunately I have to check even when mce_true, consider consteval int bar (int i) { if (i != 1) throw 1; return 0; } constexpr int a = 0 ? bar(3) : 3; > For mce_unknown I guess we'd want > to set *non_constant_p instead of giving an error. I did not do this because I haven't found a case where it would make a difference. > This is a problem. Maybe we want cp_fold_r to recurse into the arms of a > COND_EXPR before folding them away? Maybe only if we know we've seen an > immediate function? Hopefully resolved now. > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > > index 8bd5c4a47f8..af4f98b1fe1 100644 > > --- a/gcc/cp/constexpr.cc > > +++ b/gcc/cp/constexpr.cc > > @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > > unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); > > unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; > > + /* Make sure we fold std::is_constant_evaluated to true in an > > + immediate function. */ > > + if (immediate_invocation_p (fun)) > > I think this should just check DECL_IMMEDIATE_FUNCTION_P, the context > doesn't matter. Fixed. And now I don't need to export immediate_invocation_p. > > + call_ctx.manifestly_const_eval = mce_true; > > + > > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc > > index 206e791fcfd..29132aad158 100644 > > --- a/gcc/cp/cp-gimplify.cc > > +++ b/gcc/cp/cp-gimplify.cc > > @@ -1058,9 +1058,21 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) > > } > > break; > > + /* Expand immediate invocations. */ > > + case CALL_EXPR: > > + case AGGR_INIT_EXPR: > > + if (!in_immediate_context ()) > > As you mentioned in your followup, we shouldn't need to check this because > we don't call cp_fold_r in immediate context. Fixed. > > + if (tree fn = cp_get_callee (stmt)) > > + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) > > + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) > > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > > + *stmt_p = stmt = cxx_constant_value (stmt); > > + break; > > + > > case ADDR_EXPR: > > if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL > > - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) > > + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) > > + && !in_immediate_context ()) > > Likewise. Fixed. > > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > > index 799183dc646..7dfb6de2da3 100644 > > --- a/gcc/cp/tree.cc > > +++ b/gcc/cp/tree.cc > > @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) > > variables. */ > > static tree > > -bot_replace (tree* t, int* walk_subtrees, void* data_) > > +bot_replace (tree* t, int*, void* data_) > > Generally we keep the parameter name as a comment like > int */*walk_subtrees*/ I reaaally mislike that but ok, changed. > > diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C > > index d1845da9e58..9fa95295c43 100644 > > --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C > > +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C > > @@ -65,7 +65,7 @@ qux (int x) > > int r = 0; > > if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } > > { > > - r += foo (x); // { dg-error "'x' is not a constant expression" } > > + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } > > This whole function should have a comment that these errors are not required > because qux is never instantiated. Added. > > diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C > > index 2f68ec0f892..9fd32dcab7b 100644 > > --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C > > +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C > > @@ -5,25 +5,25 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n > > constexpr int a = bar (1); > > constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } > > -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } > > +constexpr int c = 0 ? bar (3) : 1; > > As discussed above, we need to keep this diagnostic and the others like it. Should be fixed. > Let's also add a test with the > > template <typename T, typename F> > constexpr bool is_not(T t, F f) { > return not f(t); > } > > consteval bool is_even(int i) { return i % 2 == 0; } > > static_assert(is_not(5, is_even)); // ok > > example from the paper. Added. I've also added consteval34.C which happened to crash clang++: https://github.com/llvm/llvm-project/issues/65520 Thanks. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. Since in cxx_* I can't rely on current_function_decl being available, I've added another walk: a new overload for in_immediate_context that looks into constexpr_ctx. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (in_immediate_context): No longer static. (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (in_immediate_context): New overload. (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test. --- gcc/cp/call.cc | 40 ++-------- gcc/cp/constexpr.cc | 38 ++++++++- gcc/cp/cp-gimplify.cc | 79 +++++++++++++++---- gcc/cp/cp-tree.h | 6 ++ gcc/cp/tree.cc | 23 +----- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 ++ gcc/testsuite/g++.dg/cpp2a/consteval11.C | 23 +++--- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 ++++++++ gcc/testsuite/g++.dg/cpp2a/consteval34.C | 18 +++++ gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 +++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 15 files changed, 203 insertions(+), 90 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 399345307ea..1b99967e4cf 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10436,6 +10436,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10453,41 +10457,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 0ca4370deab..397d5c7ec3f 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -2311,6 +2311,29 @@ cxx_dynamic_cast_fn_p (tree fndecl) && CP_DECL_CONTEXT (fndecl) == abi_node); } +/* Return true if we are in the body of a consteval function. + This is in addition to in_immediate_context because that + uses current_function_decl which may not be available. CTX is + the current constexpr context. */ + +static bool +in_immediate_context (const constexpr_ctx *ctx) +{ + if (in_immediate_context ()) + return true; + + while (ctx) + { + if (ctx->call + && ctx->call->fundef + && DECL_IMMEDIATE_FUNCTION_P (ctx->call->fundef->decl)) + return true; + ctx = ctx->parent; + } + + return false; +} + /* Often, we have an expression in the form of address + offset, e.g. "&_ZTV1A + 16". Extract the object from it, i.e. "_ZTV1A". */ @@ -3135,6 +3158,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (DECL_IMMEDIATE_FUNCTION_P (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) @@ -3807,8 +3835,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, } /* Subroutine of cxx_eval_constant_expression. - Attempt to evaluate condition expressions. Dead branches are not - looked into. */ + Attempt to evaluate condition expressions. */ static tree cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, @@ -3837,12 +3864,17 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, boolean_type_node); } /* Don't VERIFY_CONSTANT the other operands. */ - if (integer_zerop (val)) + const bool zero_p = integer_zerop (val); + if (zero_p) val = TREE_OPERAND (t, 2); else val = TREE_OPERAND (t, 1); if (TREE_CODE (t) == IF_STMT && !val) val = void_node; + + if (!in_immediate_context (ctx)) + cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2)); + /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still serve as the initializer for the same object as the outer TARGET_EXPR, as in diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..266bc50b68b 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1000,7 +1000,7 @@ cp_genericize_target_expr (tree *stmt_p) replacement when cp_folding TARGET_EXPR to preserve the invariant that AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ -bool +static bool maybe_replace_decl (tree *tp, tree decl, tree replacement) { if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) @@ -1029,22 +1029,33 @@ struct cp_genericize_data bool handle_invisiref_parm_p; }; -/* Perform any pre-gimplification folding of C++ front end trees to - GENERIC. - Note: The folding of non-omp cases is something to move into - the middle-end. As for now we have most foldings only on GENERIC - in fold-const, we need to perform this before transformation to - GIMPLE-form. */ +/* A subroutine of cp_fold_r to handle immediate functions. */ static tree -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) { - cp_fold_data *data = (cp_fold_data*)data_; + auto data = static_cast<cp_fold_data *>(data_); tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); - switch (code) + /* No need to look into types or unevaluated operands. + NB: This affects cp_fold_r as well. */ + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) { + *walk_subtrees = 0; + return NULL_TREE; + } + + switch (TREE_CODE (stmt)) + { + /* Unfortunately we must handle code like + false ? bar () : 42 + where we have to check bar too. */ + case COND_EXPR: + cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data); + if (TREE_OPERAND (stmt, 2)) + cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data); + break; + case PTRMEM_CST: if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) @@ -1058,6 +1069,16 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + *stmt_p = stmt = cxx_constant_value (stmt); + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) @@ -1074,6 +1095,34 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } + return NULL_TREE; +} + +/* A wrapper around cp_fold_immediate_r. */ + +void +cp_fold_immediate (tree *tp) +{ + cp_fold_data data (ff_mce_false); + cp_walk_tree (tp, cp_fold_immediate_r, &data, nullptr); +} + +/* Perform any pre-gimplification folding of C++ front end trees to + GENERIC. + Note: The folding of non-omp cases is something to move into + the middle-end. As for now we have most foldings only on GENERIC + in fold-const, we need to perform this before transformation to + GIMPLE-form. */ + +static tree +cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +{ + cp_fold_data *data = (cp_fold_data*)data_; + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + cp_fold_immediate_r (stmt_p, walk_subtrees, data); + *stmt_p = stmt = cp_fold (*stmt_p, data->flags); if (data->pset.add (stmt)) @@ -1084,7 +1133,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) always the same tree, which the first time cp_fold_r has been called on it had the subtrees walked. */ *walk_subtrees = 0; - return NULL; + return NULL_TREE; } code = TREE_CODE (stmt); @@ -1136,7 +1185,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; case IF_STMT: if (IF_STMT_CONSTEVAL_P (stmt)) @@ -1146,7 +1195,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL); cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; } break; @@ -1183,7 +1232,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } - return NULL; + return NULL_TREE; } /* Fold ALL the trees! FIXME we should be able to remove this, but diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..dc3efc8a472 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -8354,6 +8359,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); extern tree fold_builtin_source_location (const_tree); extern tree get_source_location_impl_type (); +extern void cp_fold_immediate (tree *); /* in name-lookup.cc */ extern tree strip_using_decl (tree); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..eaf882f8854 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int */*walk_subtrees*/, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..b2c5472b7de 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -58,6 +58,7 @@ baz (int x) return r; } +// This function is not instantiated so NDR. template <typename T> constexpr int qux (int x) @@ -65,7 +66,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..94b26cd85c4 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C new file mode 100644 index 00000000000..022eb4e76aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++20 } } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int +foo (bool b) +{ + return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" } +} + +static_assert (foo (false) == 2); + +void +g () +{ + __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" } + int a2[sizeof (bar(3))]; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval35.C b/gcc/testsuite/g++.dg/cpp2a/consteval35.C new file mode 100644 index 00000000000..59d23ac482b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval35.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++20 } } + +template <typename T, typename F> +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // ok diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..aa75ba37849 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -15,10 +15,11 @@ void qux () int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } } +// This function is not instantiated so NDR. template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 09f27ba86e3..06f1d96d9b7 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -14,6 +14,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: 971f119f0832cd1f3042d73a11f5a1be2361fc3f -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2] c++: Move consteval folding to cp_fold_r 2023-09-07 15:23 ` [PATCH v2] " Marek Polacek @ 2023-09-07 18:32 ` Jason Merrill 2023-09-08 18:24 ` [PATCH v3] " Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-07 18:32 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/7/23 11:23, Marek Polacek wrote: > On Tue, Sep 05, 2023 at 04:36:34PM -0400, Jason Merrill wrote: >> On 9/5/23 15:59, Marek Polacek wrote: >>> On Tue, Sep 05, 2023 at 10:52:04AM -0400, Jason Merrill wrote: >>>> On 9/1/23 13:23, Marek Polacek wrote: >>>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? >>>>> >>>>> -- >8 -- >>>>> >>>>> In the review of P2564: >>>>> <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> >>>>> it turned out that in order to correctly handle an example in the paper, >>>>> we should stop doing immediate evaluation in build_over_call and >>>>> bot_replace, and instead do it in cp_fold_r. This patch does that. >>>>> >>>>> Another benefit is that this is a pretty significant simplification, at >>>>> least in my opinion. Also, this fixes the c++/110997 ICE (but the test >>>>> doesn't compile yet). >>>>> >>>>> The main drawback seems to be that cp_fold_r doesn't process as much >>>>> code as we did before: uninstantiated templates >>>> >>>> That's acceptable, it's an optional diagnostic. >>>> >>>>> and things like "false ? foo () : 1". >>>> >>>> This is a problem. Maybe we want cp_fold_r to recurse into the arms of a >>>> COND_EXPR before folding them away? Maybe only if we know we've seen an >>>> immediate function? >>> >>> Unfortunately we had already thrown the dead branch away when we got to >>> cp_fold_r. I wonder if we have to adjust cxx_eval_conditional_expression >>> to call cp_fold_r on the dead branch too, >> >> Hmm, I guess so. >> >>> perhaps with a new ff_ flag to skip the whole second switch in cp_fold_r? >> >> Or factor out the immediate function handling to a separate walk function >> that cp_fold_r also calls? > > I did that. > >>> But then it's possible that the in_immediate_context checks have to stay. >> >> We can just not do the walk in immediate (or mce_true) context, like we >> currently avoid calling cp_fold_function. > > Right. Unfortunately I have to check even when mce_true, consider > > consteval int bar (int i) { if (i != 1) throw 1; return 0; } > constexpr int a = 0 ? bar(3) : 3; I disagree; the call is in a manifestly constant-evaluated expression, and so is now considered an immediate function context, and we should accept that example. >> For mce_unknown I guess we'd want >> to set *non_constant_p instead of giving an error. > > I did not do this because I haven't found a case where it would make > a difference. I think it will given the above comment. > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index 0ca4370deab..397d5c7ec3f 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -2311,6 +2311,29 @@ cxx_dynamic_cast_fn_p (tree fndecl) > && CP_DECL_CONTEXT (fndecl) == abi_node); > } > > +/* Return true if we are in the body of a consteval function. > + This is in addition to in_immediate_context because that > + uses current_function_decl which may not be available. CTX is > + the current constexpr context. */ > + > +static bool > +in_immediate_context (const constexpr_ctx *ctx) > +{ > + if (in_immediate_context ()) > + return true; Can't we check for mce_true here instead of looking at the call chain? > +/* A wrapper around cp_fold_immediate_r. */ > + > +void > +cp_fold_immediate (tree *tp) > +{ Maybe return early if consteval isn't supported in the active standard? Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v3] c++: Move consteval folding to cp_fold_r 2023-09-07 18:32 ` Jason Merrill @ 2023-09-08 18:24 ` Marek Polacek 2023-09-12 21:26 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-08 18:24 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Thu, Sep 07, 2023 at 02:32:51PM -0400, Jason Merrill wrote: > On 9/7/23 11:23, Marek Polacek wrote: > > On Tue, Sep 05, 2023 at 04:36:34PM -0400, Jason Merrill wrote: > > > On 9/5/23 15:59, Marek Polacek wrote: > > > > On Tue, Sep 05, 2023 at 10:52:04AM -0400, Jason Merrill wrote: > > > > > On 9/1/23 13:23, Marek Polacek wrote: > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > > > > > > > > > -- >8 -- > > > > > > > > > > > > In the review of P2564: > > > > > > <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> > > > > > > it turned out that in order to correctly handle an example in the paper, > > > > > > we should stop doing immediate evaluation in build_over_call and > > > > > > bot_replace, and instead do it in cp_fold_r. This patch does that. > > > > > > > > > > > > Another benefit is that this is a pretty significant simplification, at > > > > > > least in my opinion. Also, this fixes the c++/110997 ICE (but the test > > > > > > doesn't compile yet). > > > > > > > > > > > > The main drawback seems to be that cp_fold_r doesn't process as much > > > > > > code as we did before: uninstantiated templates > > > > > > > > > > That's acceptable, it's an optional diagnostic. > > > > > > > > > > > and things like "false ? foo () : 1". > > > > > > > > > > This is a problem. Maybe we want cp_fold_r to recurse into the arms of a > > > > > COND_EXPR before folding them away? Maybe only if we know we've seen an > > > > > immediate function? > > > > > > > > Unfortunately we had already thrown the dead branch away when we got to > > > > cp_fold_r. I wonder if we have to adjust cxx_eval_conditional_expression > > > > to call cp_fold_r on the dead branch too, > > > > > > Hmm, I guess so. > > > > > > > perhaps with a new ff_ flag to skip the whole second switch in cp_fold_r? > > > > > > Or factor out the immediate function handling to a separate walk function > > > that cp_fold_r also calls? > > > > I did that. > > > > But then it's possible that the in_immediate_context checks have to stay. > > > > > > We can just not do the walk in immediate (or mce_true) context, like we > > > currently avoid calling cp_fold_function. > > > > Right. Unfortunately I have to check even when mce_true, consider > > > > consteval int bar (int i) { if (i != 1) throw 1; return 0; } > > constexpr int a = 0 ? bar(3) : 3; > > I disagree; the call is in a manifestly constant-evaluated expression, and > so is now considered an immediate function context, and we should accept > that example. Ack. I was still living in pre-P2564 world. > > > For mce_unknown I guess we'd want > > > to set *non_constant_p instead of giving an error. > > > > I did not do this because I haven't found a case where it would make > > a difference. > > I think it will given the above comment. Correct. For instance, in: consteval int bar (int i) { if (i != 1) throw 1; return 0; } constexpr int foo (bool b) { return b ? bar (3) : 2; } static_assert (foo (false) == 2); we should complain only once. I've implemented your suggestion to set *non_constant_p instead of giving an error for mce_unknown. > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > > index 0ca4370deab..397d5c7ec3f 100644 > > --- a/gcc/cp/constexpr.cc > > +++ b/gcc/cp/constexpr.cc > > @@ -2311,6 +2311,29 @@ cxx_dynamic_cast_fn_p (tree fndecl) > > && CP_DECL_CONTEXT (fndecl) == abi_node); > > } > > +/* Return true if we are in the body of a consteval function. > + This is in addition to in_immediate_context because that > > + uses current_function_decl which may not be available. CTX is > > + the current constexpr context. */ > > + > > +static bool > > +in_immediate_context (const constexpr_ctx *ctx) > > +{ > > + if (in_immediate_context ()) > > + return true; > > Can't we check for mce_true here instead of looking at the call chain? Yes. > > +/* A wrapper around cp_fold_immediate_r. */ > > + > > +void > > +cp_fold_immediate (tree *tp) > > +{ > > Maybe return early if consteval isn't supported in the active standard? Absolutely. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (in_immediate_context): New overload. (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test. --- gcc/cp/call.cc | 40 +------ gcc/cp/constexpr.cc | 23 +++- gcc/cp/cp-gimplify.cc | 108 ++++++++++++++---- gcc/cp/cp-tree.h | 42 ++++--- gcc/cp/tree.cc | 23 +--- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 ++ gcc/testsuite/g++.dg/cpp2a/consteval11.C | 33 +++--- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 ++++++ gcc/testsuite/g++.dg/cpp2a/consteval34.C | 18 +++ gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 ++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 15 files changed, 234 insertions(+), 119 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 399345307ea..1b99967e4cf 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10436,6 +10436,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10453,41 +10457,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 0ca4370deab..8c077aa22fe 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (DECL_IMMEDIATE_FUNCTION_P (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) @@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, } /* Subroutine of cxx_eval_constant_expression. - Attempt to evaluate condition expressions. Dead branches are not - looked into. */ + Attempt to evaluate condition expressions. */ static tree cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, @@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, boolean_type_node); } /* Don't VERIFY_CONSTANT the other operands. */ - if (integer_zerop (val)) + const bool zero_p = integer_zerop (val); + if (zero_p) val = TREE_OPERAND (t, 2); else val = TREE_OPERAND (t, 1); if (TREE_CODE (t) == IF_STMT && !val) val = void_node; + + if (!in_immediate_context () + /* P2564: a subexpression of a manifestly constant-evaluated expression + or conversion is an immediate function context. */ + && ctx->manifestly_const_eval != mce_true + && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2), + ctx->manifestly_const_eval)) + { + *non_constant_p = true; + return t; + } + /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still serve as the initializer for the same object as the outer TARGET_EXPR, as in diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..9901513461c 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1000,7 +1000,7 @@ cp_genericize_target_expr (tree *stmt_p) replacement when cp_folding TARGET_EXPR to preserve the invariant that AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ -bool +static bool maybe_replace_decl (tree *tp, tree decl, tree replacement) { if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) @@ -1029,44 +1029,73 @@ struct cp_genericize_data bool handle_invisiref_parm_p; }; -/* Perform any pre-gimplification folding of C++ front end trees to - GENERIC. - Note: The folding of non-omp cases is something to move into - the middle-end. As for now we have most foldings only on GENERIC - in fold-const, we need to perform this before transformation to - GIMPLE-form. */ +/* A subroutine of cp_fold_r to handle immediate functions. */ static tree -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) { - cp_fold_data *data = (cp_fold_data*)data_; + auto data = static_cast<cp_fold_data *>(data_); tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); + const tsubst_flags_t complain = (data->flags == ff_none ? tf_none : tf_error); - switch (code) + /* No need to look into types or unevaluated operands. + NB: This affects cp_fold_r as well. */ + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) { + *walk_subtrees = 0; + return NULL_TREE; + } + + switch (TREE_CODE (stmt)) + { + /* Unfortunately we must handle code like + false ? bar () : 42 + where we have to check bar too. */ + case COND_EXPR: + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) + return error_mark_node; + if (TREE_OPERAND (stmt, 2) + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) + return error_mark_node; + break; + case PTRMEM_CST: if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) { - if (!data->pset.add (stmt)) + if (!data->pset.add (stmt) && (complain & tf_error)) error_at (PTRMEM_CST_LOCATION (stmt), "taking address of an immediate function %qD", PTRMEM_CST_MEMBER (stmt)); stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + return error_mark_node; } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + { + *stmt_p = stmt = cxx_constant_value (stmt, complain); + if (stmt == error_mark_node) + return error_mark_node; + } + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) { - error_at (EXPR_LOCATION (stmt), - "taking address of an immediate function %qD", - TREE_OPERAND (stmt, 0)); + if (complain & tf_error) + error_at (EXPR_LOCATION (stmt), + "taking address of an immediate function %qD", + TREE_OPERAND (stmt, 0)); stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + return error_mark_node; } break; @@ -1074,6 +1103,43 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } + return NULL_TREE; +} + +/* A wrapper around cp_fold_immediate_r. Return true if we found + a non-constant immediate function, or taking the address of an + immediate function. */ + +bool +cp_fold_immediate (tree *tp, mce_value manifestly_const_eval) +{ + if (cxx_dialect <= cxx17) + return false; + + fold_flags_t flags = ff_none; + if (manifestly_const_eval == mce_false) + flags |= ff_mce_false; + + cp_fold_data data (flags); + return !!cp_walk_tree (tp, cp_fold_immediate_r, &data, nullptr); +} + +/* Perform any pre-gimplification folding of C++ front end trees to + GENERIC. + Note: The folding of non-omp cases is something to move into + the middle-end. As for now we have most foldings only on GENERIC + in fold-const, we need to perform this before transformation to + GIMPLE-form. */ + +static tree +cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +{ + cp_fold_data *data = (cp_fold_data*)data_; + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + cp_fold_immediate_r (stmt_p, walk_subtrees, data); + *stmt_p = stmt = cp_fold (*stmt_p, data->flags); if (data->pset.add (stmt)) @@ -1084,7 +1150,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) always the same tree, which the first time cp_fold_r has been called on it had the subtrees walked. */ *walk_subtrees = 0; - return NULL; + return NULL_TREE; } code = TREE_CODE (stmt); @@ -1136,7 +1202,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; case IF_STMT: if (IF_STMT_CONSTEVAL_P (stmt)) @@ -1146,7 +1212,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL); cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; } break; @@ -1183,7 +1249,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } - return NULL; + return NULL_TREE; } /* Fold ALL the trees! FIXME we should be able to remove this, but diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..5084932633a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6580,6 +6585,24 @@ extern int class_dump_id; extern int module_dump_id; extern int raw_dump_id; +/* Whether the current context is manifestly constant-evaluated. + Used by the constexpr machinery to control folding of + __builtin_is_constant_evaluated. */ + +enum class mce_value +{ + /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ + mce_unknown = 0, + /* Fold it to true. */ + mce_true = 1, + /* Fold it to false. Primarily used during cp_fold_function and + cp_fully_fold_init. */ + mce_false = -1, +}; +constexpr mce_value mce_unknown = mce_value::mce_unknown; +constexpr mce_value mce_true = mce_value::mce_true; +constexpr mce_value mce_false = mce_value::mce_false; + /* in call.cc */ extern bool check_dtor_name (tree, tree); int magic_varargs_p (tree); @@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); extern tree fold_builtin_source_location (const_tree); extern tree get_source_location_impl_type (); +extern bool cp_fold_immediate (tree *, mce_value); /* in name-lookup.cc */ extern tree strip_using_decl (tree); @@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef { tree result; }; -/* Whether the current context is manifestly constant-evaluated. - Used by the constexpr machinery to control folding of - __builtin_is_constant_evaluated. */ - -enum class mce_value -{ - /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ - mce_unknown = 0, - /* Fold it to true. */ - mce_true = 1, - /* Fold it to false. Primarily used during cp_fold_function and - cp_fully_fold_init. */ - mce_false = -1, -}; -constexpr mce_value mce_unknown = mce_value::mce_unknown; -constexpr mce_value mce_true = mce_value::mce_true; -constexpr mce_value mce_false = mce_value::mce_false; - extern void fini_constexpr (void); extern bool literal_type_p (tree); extern void maybe_save_constexpr_fundef (tree); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..eaf882f8854 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int */*walk_subtrees*/, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..b2c5472b7de 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -58,6 +58,7 @@ baz (int x) return r; } +// This function is not instantiated so NDR. template <typename T> constexpr int qux (int x) @@ -65,7 +66,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..091127eabbf 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -5,21 +5,21 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } -const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } -int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; void foo () { constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } - constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } - const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C new file mode 100644 index 00000000000..022eb4e76aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++20 } } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int +foo (bool b) +{ + return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" } +} + +static_assert (foo (false) == 2); + +void +g () +{ + __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" } + int a2[sizeof (bar(3))]; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval35.C b/gcc/testsuite/g++.dg/cpp2a/consteval35.C new file mode 100644 index 00000000000..59d23ac482b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval35.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++20 } } + +template <typename T, typename F> +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // ok diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..aa75ba37849 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -15,10 +15,11 @@ void qux () int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } } +// This function is not instantiated so NDR. template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 09f27ba86e3..06f1d96d9b7 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -14,6 +14,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: b96b554592c5cbb6a2c1797ffcb5706fd295f4fd -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v3] c++: Move consteval folding to cp_fold_r 2023-09-08 18:24 ` [PATCH v3] " Marek Polacek @ 2023-09-12 21:26 ` Jason Merrill 2023-09-13 20:56 ` [PATCH v4] " Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-12 21:26 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/8/23 14:24, Marek Polacek wrote: > On Thu, Sep 07, 2023 at 02:32:51PM -0400, Jason Merrill wrote: >> On 9/7/23 11:23, Marek Polacek wrote: >>> On Tue, Sep 05, 2023 at 04:36:34PM -0400, Jason Merrill wrote: >>>> On 9/5/23 15:59, Marek Polacek wrote: >>>>> On Tue, Sep 05, 2023 at 10:52:04AM -0400, Jason Merrill wrote: >>>>>> On 9/1/23 13:23, Marek Polacek wrote: >>>>>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? >>>>>>> >>>>>>> -- >8 -- >>>>>>> >>>>>>> In the review of P2564: >>>>>>> <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> >>>>>>> it turned out that in order to correctly handle an example in the paper, >>>>>>> we should stop doing immediate evaluation in build_over_call and >>>>>>> bot_replace, and instead do it in cp_fold_r. This patch does that. >>>>>>> >>>>>>> Another benefit is that this is a pretty significant simplification, at >>>>>>> least in my opinion. Also, this fixes the c++/110997 ICE (but the test >>>>>>> doesn't compile yet). >>>>>>> >>>>>>> The main drawback seems to be that cp_fold_r doesn't process as much >>>>>>> code as we did before: uninstantiated templates >>>>>> >>>>>> That's acceptable, it's an optional diagnostic. >>>>>> >>>>>>> and things like "false ? foo () : 1". >>>>>> >>>>>> This is a problem. Maybe we want cp_fold_r to recurse into the arms of a >>>>>> COND_EXPR before folding them away? Maybe only if we know we've seen an >>>>>> immediate function? >>>>> >>>>> Unfortunately we had already thrown the dead branch away when we got to >>>>> cp_fold_r. I wonder if we have to adjust cxx_eval_conditional_expression >>>>> to call cp_fold_r on the dead branch too, >>>> >>>> Hmm, I guess so. >>>> >>>>> perhaps with a new ff_ flag to skip the whole second switch in cp_fold_r? >>>> >>>> Or factor out the immediate function handling to a separate walk function >>>> that cp_fold_r also calls? >>> >>> I did that. >>>>> But then it's possible that the in_immediate_context checks have to stay. >>>> >>>> We can just not do the walk in immediate (or mce_true) context, like we >>>> currently avoid calling cp_fold_function. >>> >>> Right. Unfortunately I have to check even when mce_true, consider >>> >>> consteval int bar (int i) { if (i != 1) throw 1; return 0; } >>> constexpr int a = 0 ? bar(3) : 3; >> >> I disagree; the call is in a manifestly constant-evaluated expression, and >> so is now considered an immediate function context, and we should accept >> that example. > > Ack. I was still living in pre-P2564 world. > >>>> For mce_unknown I guess we'd want >>>> to set *non_constant_p instead of giving an error. >>> >>> I did not do this because I haven't found a case where it would make >>> a difference. >> >> I think it will given the above comment. > > Correct. For instance, in: > > consteval int bar (int i) { if (i != 1) throw 1; return 0; } > > constexpr int > foo (bool b) > { > return b ? bar (3) : 2; > } > > static_assert (foo (false) == 2); > > we should complain only once. I've implemented your suggestion to set > *non_constant_p instead of giving an error for mce_unknown. > >>> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc >>> index 0ca4370deab..397d5c7ec3f 100644 >>> --- a/gcc/cp/constexpr.cc >>> +++ b/gcc/cp/constexpr.cc >>> @@ -2311,6 +2311,29 @@ cxx_dynamic_cast_fn_p (tree fndecl) >>> && CP_DECL_CONTEXT (fndecl) == abi_node); >>> } >>> +/* Return true if we are in the body of a consteval function. > + This is in addition to in_immediate_context because that >>> + uses current_function_decl which may not be available. CTX is >>> + the current constexpr context. */ >>> + >>> +static bool >>> +in_immediate_context (const constexpr_ctx *ctx) >>> +{ >>> + if (in_immediate_context ()) >>> + return true; >> >> Can't we check for mce_true here instead of looking at the call chain? > > Yes. > >>> +/* A wrapper around cp_fold_immediate_r. */ >>> + >>> +void >>> +cp_fold_immediate (tree *tp) >>> +{ >> >> Maybe return early if consteval isn't supported in the active standard? > > Absolutely. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > In the review of P2564: > <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> > it turned out that in order to correctly handle an example in the paper, > we should stop doing immediate evaluation in build_over_call and > bot_replace, and instead do it in cp_fold_r. This patch does that. > > Another benefit is that this is a pretty significant simplification, at > least in my opinion. Also, this fixes the c++/110997 ICE (but the test > doesn't compile yet). > > The main drawback seems to be that cp_fold_r doesn't process > uninstantiated templates. We still have to handle things like > "false ? foo () : 1". To that end, I've added cp_fold_immediate, called > on dead branches in cxx_eval_conditional_expression. > > You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This > is to detect > > *(&foo)) () > (s.*&S::foo) () > > which were deemed ill-formed. > > gcc/cp/ChangeLog: > > * call.cc (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle > immediate_invocation_p here. > * constexpr.cc (in_immediate_context): New overload. > (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. > (cxx_eval_conditional_expression): Call cp_fold_immediate. > * cp-gimplify.cc (maybe_replace_decl): Make static. > (cp_fold_r): Expand immediate invocations. > (cp_fold_immediate_r): New. > (cp_fold_immediate): New. > * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. > (cp_fold_immediate): Declare. > * tree.cc (bot_replace): Don't handle immediate invocations here. > > libstdc++-v3/ChangeLog: > > * testsuite/20_util/allocator/105975.cc: Add dg-error. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp23/consteval-if2.C: Add xfail. > * g++.dg/cpp2a/consteval-memfn1.C: Adjust. > * g++.dg/cpp2a/consteval11.C: Remove dg-message. > * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. > * g++.dg/cpp2a/consteval9.C: Remove dg-message. > * g++.dg/cpp2a/consteval32.C: New test. > * g++.dg/cpp2a/consteval33.C: New test. > * g++.dg/cpp2a/consteval34.C: New test. > * g++.dg/cpp2a/consteval35.C: New test. > --- > gcc/cp/call.cc | 40 +------ > gcc/cp/constexpr.cc | 23 +++- > gcc/cp/cp-gimplify.cc | 108 ++++++++++++++---- > gcc/cp/cp-tree.h | 42 ++++--- > gcc/cp/tree.cc | 23 +--- > gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- > gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 ++ > gcc/testsuite/g++.dg/cpp2a/consteval11.C | 33 +++--- > gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- > gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + > gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 ++++++ > gcc/testsuite/g++.dg/cpp2a/consteval34.C | 18 +++ > gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 ++ > gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- > .../testsuite/20_util/allocator/105975.cc | 2 +- > 15 files changed, 234 insertions(+), 119 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index 0ca4370deab..8c077aa22fe 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, > boolean_type_node); > } > /* Don't VERIFY_CONSTANT the other operands. */ > - if (integer_zerop (val)) > + const bool zero_p = integer_zerop (val); > + if (zero_p) > val = TREE_OPERAND (t, 2); > else > val = TREE_OPERAND (t, 1); > if (TREE_CODE (t) == IF_STMT && !val) > val = void_node; > + > + if (!in_immediate_context () > + /* P2564: a subexpression of a manifestly constant-evaluated expression > + or conversion is an immediate function context. */ > + && ctx->manifestly_const_eval != mce_true I might check this first as a tiny optimization. > + && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2), > + ctx->manifestly_const_eval)) > + { > + *non_constant_p = true; > + return t; > + } > + > /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still > serve as the initializer for the same object as the outer TARGET_EXPR, > as in > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc > index 206e791fcfd..9901513461c 100644 > --- a/gcc/cp/cp-gimplify.cc > +++ b/gcc/cp/cp-gimplify.cc > @@ -1000,7 +1000,7 @@ cp_genericize_target_expr (tree *stmt_p) > replacement when cp_folding TARGET_EXPR to preserve the invariant that > AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ > > -bool > +static bool > maybe_replace_decl (tree *tp, tree decl, tree replacement) > { > if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) > @@ -1029,44 +1029,73 @@ struct cp_genericize_data > bool handle_invisiref_parm_p; > }; > > -/* Perform any pre-gimplification folding of C++ front end trees to > - GENERIC. > - Note: The folding of non-omp cases is something to move into > - the middle-end. As for now we have most foldings only on GENERIC > - in fold-const, we need to perform this before transformation to > - GIMPLE-form. */ > +/* A subroutine of cp_fold_r to handle immediate functions. */ > > static tree > -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) > +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) > { > - cp_fold_data *data = (cp_fold_data*)data_; > + auto data = static_cast<cp_fold_data *>(data_); > tree stmt = *stmt_p; > - enum tree_code code = TREE_CODE (stmt); > + const tsubst_flags_t complain = (data->flags == ff_none ? tf_none : tf_error); > > - switch (code) > + /* No need to look into types or unevaluated operands. > + NB: This affects cp_fold_r as well. */ > + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) > { > + *walk_subtrees = 0; > + return NULL_TREE; > + } > + > + switch (TREE_CODE (stmt)) > + { > + /* Unfortunately we must handle code like > + false ? bar () : 42 > + where we have to check bar too. */ > + case COND_EXPR: > + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) > + return error_mark_node; > + if (TREE_OPERAND (stmt, 2) > + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) > + return error_mark_node; Is this necessary? Doesn't walk_tree already walk into the arms of COND_EXPR? > + break; > + > case PTRMEM_CST: > if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL > && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) > { > - if (!data->pset.add (stmt)) > + if (!data->pset.add (stmt) && (complain & tf_error)) > error_at (PTRMEM_CST_LOCATION (stmt), > "taking address of an immediate function %qD", > PTRMEM_CST_MEMBER (stmt)); > stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); It looks like this will overwrite *stmt_p even if we didn't give an error. > - break; > + return error_mark_node; > } > break; > > + /* Expand immediate invocations. */ > + case CALL_EXPR: > + case AGGR_INIT_EXPR: > + if (tree fn = cp_get_callee (stmt)) > + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) > + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > + { > + *stmt_p = stmt = cxx_constant_value (stmt, complain); Likewise. > + if (stmt == error_mark_node) > + return error_mark_node; > + } > + break; > + > case ADDR_EXPR: > if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL > && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) > { > - error_at (EXPR_LOCATION (stmt), > - "taking address of an immediate function %qD", > - TREE_OPERAND (stmt, 0)); > + if (complain & tf_error) > + error_at (EXPR_LOCATION (stmt), > + "taking address of an immediate function %qD", > + TREE_OPERAND (stmt, 0)); > stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); Likewise. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v4] c++: Move consteval folding to cp_fold_r 2023-09-12 21:26 ` Jason Merrill @ 2023-09-13 20:56 ` Marek Polacek 2023-09-13 21:57 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-13 20:56 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: > On 9/8/23 14:24, Marek Polacek wrote: > > + if (!in_immediate_context () > > + /* P2564: a subexpression of a manifestly constant-evaluated expression > > + or conversion is an immediate function context. */ > > + && ctx->manifestly_const_eval != mce_true > > I might check this first as a tiny optimization. Done. > > + switch (TREE_CODE (stmt)) > > + { > > + /* Unfortunately we must handle code like > > + false ? bar () : 42 > > + where we have to check bar too. */ > > + case COND_EXPR: > > + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) > > + return error_mark_node; > > + if (TREE_OPERAND (stmt, 2) > > + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) > > + return error_mark_node; > > Is this necessary? Doesn't walk_tree already walk into the arms of > COND_EXPR? Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into a constant before we see it here. I've added a comment saying just that. > > + break; > > + > > case PTRMEM_CST: > > if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL > > && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) > > { > > - if (!data->pset.add (stmt)) > > + if (!data->pset.add (stmt) && (complain & tf_error)) > > error_at (PTRMEM_CST_LOCATION (stmt), > > "taking address of an immediate function %qD", > > PTRMEM_CST_MEMBER (stmt)); > > stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); > > It looks like this will overwrite *stmt_p even if we didn't give an error. I suppose that could result in missing errors, adjusted. And there's no point in setting stmt. > > - break; > > + return error_mark_node; > > } > > break; > > + /* Expand immediate invocations. */ > > + case CALL_EXPR: > > + case AGGR_INIT_EXPR: > > + if (tree fn = cp_get_callee (stmt)) > > + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) > > + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) > > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > > + { > > + *stmt_p = stmt = cxx_constant_value (stmt, complain); > > Likewise. I think we have to keep setting *stmt_p to actually evaluate consteval functions. But again, no need to set stmt. > > + if (stmt == error_mark_node) > > + return error_mark_node; > > + } > > + break; > > + > > case ADDR_EXPR: > > if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL > > && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) > > { > > - error_at (EXPR_LOCATION (stmt), > > - "taking address of an immediate function %qD", > > - TREE_OPERAND (stmt, 0)); > > + if (complain & tf_error) > > + error_at (EXPR_LOCATION (stmt), > > + "taking address of an immediate function %qD", > > + TREE_OPERAND (stmt, 0)); > > stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); > > Likewise. Adjusted like case PTRMEM_CST. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. Since in cxx_* I can't rely on current_function_decl being available, I've added another walk: a new overload for in_immediate_context that looks into constexpr_ctx. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (in_immediate_context): No longer static. (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (in_immediate_context): New overload. (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test. --- gcc/cp/call.cc | 40 +----- gcc/cp/constexpr.cc | 23 +++- gcc/cp/cp-gimplify.cc | 123 ++++++++++++++---- gcc/cp/cp-tree.h | 42 +++--- gcc/cp/tree.cc | 23 +--- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 + gcc/testsuite/g++.dg/cpp2a/consteval11.C | 33 ++--- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 +++++ gcc/testsuite/g++.dg/cpp2a/consteval34.C | 18 +++ gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 ++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 15 files changed, 244 insertions(+), 124 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 399345307ea..1b99967e4cf 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10436,6 +10436,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10453,41 +10457,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 0ca4370deab..a673a6022f1 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (DECL_IMMEDIATE_FUNCTION_P (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) @@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, } /* Subroutine of cxx_eval_constant_expression. - Attempt to evaluate condition expressions. Dead branches are not - looked into. */ + Attempt to evaluate condition expressions. */ static tree cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, @@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, boolean_type_node); } /* Don't VERIFY_CONSTANT the other operands. */ - if (integer_zerop (val)) + const bool zero_p = integer_zerop (val); + if (zero_p) val = TREE_OPERAND (t, 2); else val = TREE_OPERAND (t, 1); if (TREE_CODE (t) == IF_STMT && !val) val = void_node; + + /* P2564: a subexpression of a manifestly constant-evaluated expression + or conversion is an immediate function context. */ + if (ctx->manifestly_const_eval != mce_true + && !in_immediate_context () + && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2), + ctx->manifestly_const_eval)) + { + *non_constant_p = true; + return t; + } + /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still serve as the initializer for the same object as the outer TARGET_EXPR, as in diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..a7117827147 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1000,7 +1000,7 @@ cp_genericize_target_expr (tree *stmt_p) replacement when cp_folding TARGET_EXPR to preserve the invariant that AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ -bool +static bool maybe_replace_decl (tree *tp, tree decl, tree replacement) { if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) @@ -1029,44 +1029,78 @@ struct cp_genericize_data bool handle_invisiref_parm_p; }; -/* Perform any pre-gimplification folding of C++ front end trees to - GENERIC. - Note: The folding of non-omp cases is something to move into - the middle-end. As for now we have most foldings only on GENERIC - in fold-const, we need to perform this before transformation to - GIMPLE-form. */ +/* A subroutine of cp_fold_r to handle immediate functions. */ static tree -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) { - cp_fold_data *data = (cp_fold_data*)data_; + auto data = static_cast<cp_fold_data *>(data_); tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); + const tsubst_flags_t complain = (data->flags == ff_none ? tf_none : tf_error); - switch (code) + /* No need to look into types or unevaluated operands. + NB: This affects cp_fold_r as well. */ + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) + { + *walk_subtrees = 0; + return NULL_TREE; + } + + switch (TREE_CODE (stmt)) { + /* Unfortunately we must handle code like + false ? bar () : 42 + where we have to check bar too. The cp_fold call in cp_fold_r could + fold the ?: into a constant before we see it here. */ + case COND_EXPR: + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) + return error_mark_node; + if (TREE_OPERAND (stmt, 2) + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) + return error_mark_node; + break; + case PTRMEM_CST: if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) { - if (!data->pset.add (stmt)) - error_at (PTRMEM_CST_LOCATION (stmt), - "taking address of an immediate function %qD", - PTRMEM_CST_MEMBER (stmt)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (!data->pset.add (stmt) && (complain & tf_error)) + { + error_at (PTRMEM_CST_LOCATION (stmt), + "taking address of an immediate function %qD", + PTRMEM_CST_MEMBER (stmt)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + { + *stmt_p = cxx_constant_value (stmt, complain); + if (*stmt_p == error_mark_node) + return error_mark_node; + } + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) { - error_at (EXPR_LOCATION (stmt), - "taking address of an immediate function %qD", - TREE_OPERAND (stmt, 0)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (complain & tf_error) + { + error_at (EXPR_LOCATION (stmt), + "taking address of an immediate function %qD", + TREE_OPERAND (stmt, 0)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; @@ -1074,6 +1108,43 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } + return NULL_TREE; +} + +/* A wrapper around cp_fold_immediate_r. Return true if we found + a non-constant immediate function, or taking the address of an + immediate function. */ + +bool +cp_fold_immediate (tree *tp, mce_value manifestly_const_eval) +{ + if (cxx_dialect <= cxx17) + return false; + + fold_flags_t flags = ff_none; + if (manifestly_const_eval == mce_false) + flags |= ff_mce_false; + + cp_fold_data data (flags); + return !!cp_walk_tree (tp, cp_fold_immediate_r, &data, nullptr); +} + +/* Perform any pre-gimplification folding of C++ front end trees to + GENERIC. + Note: The folding of non-omp cases is something to move into + the middle-end. As for now we have most foldings only on GENERIC + in fold-const, we need to perform this before transformation to + GIMPLE-form. */ + +static tree +cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +{ + cp_fold_data *data = (cp_fold_data*)data_; + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + cp_fold_immediate_r (stmt_p, walk_subtrees, data); + *stmt_p = stmt = cp_fold (*stmt_p, data->flags); if (data->pset.add (stmt)) @@ -1084,7 +1155,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) always the same tree, which the first time cp_fold_r has been called on it had the subtrees walked. */ *walk_subtrees = 0; - return NULL; + return NULL_TREE; } code = TREE_CODE (stmt); @@ -1136,7 +1207,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; case IF_STMT: if (IF_STMT_CONSTEVAL_P (stmt)) @@ -1146,7 +1217,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL); cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; } break; @@ -1183,7 +1254,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } - return NULL; + return NULL_TREE; } /* Fold ALL the trees! FIXME we should be able to remove this, but diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..5084932633a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6580,6 +6585,24 @@ extern int class_dump_id; extern int module_dump_id; extern int raw_dump_id; +/* Whether the current context is manifestly constant-evaluated. + Used by the constexpr machinery to control folding of + __builtin_is_constant_evaluated. */ + +enum class mce_value +{ + /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ + mce_unknown = 0, + /* Fold it to true. */ + mce_true = 1, + /* Fold it to false. Primarily used during cp_fold_function and + cp_fully_fold_init. */ + mce_false = -1, +}; +constexpr mce_value mce_unknown = mce_value::mce_unknown; +constexpr mce_value mce_true = mce_value::mce_true; +constexpr mce_value mce_false = mce_value::mce_false; + /* in call.cc */ extern bool check_dtor_name (tree, tree); int magic_varargs_p (tree); @@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); extern tree fold_builtin_source_location (const_tree); extern tree get_source_location_impl_type (); +extern bool cp_fold_immediate (tree *, mce_value); /* in name-lookup.cc */ extern tree strip_using_decl (tree); @@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef { tree result; }; -/* Whether the current context is manifestly constant-evaluated. - Used by the constexpr machinery to control folding of - __builtin_is_constant_evaluated. */ - -enum class mce_value -{ - /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ - mce_unknown = 0, - /* Fold it to true. */ - mce_true = 1, - /* Fold it to false. Primarily used during cp_fold_function and - cp_fully_fold_init. */ - mce_false = -1, -}; -constexpr mce_value mce_unknown = mce_value::mce_unknown; -constexpr mce_value mce_true = mce_value::mce_true; -constexpr mce_value mce_false = mce_value::mce_false; - extern void fini_constexpr (void); extern bool literal_type_p (tree); extern void maybe_save_constexpr_fundef (tree); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..eaf882f8854 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int */*walk_subtrees*/, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..b2c5472b7de 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -58,6 +58,7 @@ baz (int x) return r; } +// This function is not instantiated so NDR. template <typename T> constexpr int qux (int x) @@ -65,7 +66,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..091127eabbf 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -5,21 +5,21 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } -const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } -int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; void foo () { constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } - constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } - const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C new file mode 100644 index 00000000000..022eb4e76aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++20 } } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int +foo (bool b) +{ + return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" } +} + +static_assert (foo (false) == 2); + +void +g () +{ + __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" } + int a2[sizeof (bar(3))]; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval35.C b/gcc/testsuite/g++.dg/cpp2a/consteval35.C new file mode 100644 index 00000000000..59d23ac482b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval35.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++20 } } + +template <typename T, typename F> +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // ok diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..aa75ba37849 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -15,10 +15,11 @@ void qux () int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } } +// This function is not instantiated so NDR. template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 09f27ba86e3..06f1d96d9b7 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -14,6 +14,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: 92456291849fe88303bbcab366f41dcd4a885ad5 -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v4] c++: Move consteval folding to cp_fold_r 2023-09-13 20:56 ` [PATCH v4] " Marek Polacek @ 2023-09-13 21:57 ` Jason Merrill 2023-09-14 0:02 ` [PATCH v5] " Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-13 21:57 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/13/23 16:56, Marek Polacek wrote: > On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: >> On 9/8/23 14:24, Marek Polacek wrote: > >>> + switch (TREE_CODE (stmt)) >>> + { >>> + /* Unfortunately we must handle code like >>> + false ? bar () : 42 >>> + where we have to check bar too. */ >>> + case COND_EXPR: >>> + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) >>> + return error_mark_node; >>> + if (TREE_OPERAND (stmt, 2) >>> + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) >>> + return error_mark_node; >> >> Is this necessary? Doesn't walk_tree already walk into the arms of >> COND_EXPR? > > Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into > a constant before we see it here. I've added a comment saying just that. Ah. But in that case I guess we need to walk into the arms, not just check the top-level expression in them. But maybe cp_fold_r should do that before the cp_fold, instead of this function? >>> + break; >>> + >>> case PTRMEM_CST: >>> if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL >>> && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) >>> { >>> - if (!data->pset.add (stmt)) >>> + if (!data->pset.add (stmt) && (complain & tf_error)) >>> error_at (PTRMEM_CST_LOCATION (stmt), >>> "taking address of an immediate function %qD", >>> PTRMEM_CST_MEMBER (stmt)); >>> stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); >> >> It looks like this will overwrite *stmt_p even if we didn't give an error. > > I suppose that could result in missing errors, adjusted. And there's no > point in setting stmt. > >>> - break; >>> + return error_mark_node; >>> } >>> break; >>> + /* Expand immediate invocations. */ >>> + case CALL_EXPR: >>> + case AGGR_INIT_EXPR: >>> + if (tree fn = cp_get_callee (stmt)) >>> + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) >>> + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) >>> + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) >>> + { >>> + *stmt_p = stmt = cxx_constant_value (stmt, complain); >> >> Likewise. > > I think we have to keep setting *stmt_p to actually evaluate consteval > functions. But only when it succeeds; we don't want to set it to error_mark_node if we aren't complaining. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v5] c++: Move consteval folding to cp_fold_r 2023-09-13 21:57 ` Jason Merrill @ 2023-09-14 0:02 ` Marek Polacek 2023-09-15 18:08 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-14 0:02 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Wed, Sep 13, 2023 at 05:57:47PM -0400, Jason Merrill wrote: > On 9/13/23 16:56, Marek Polacek wrote: > > On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: > > > On 9/8/23 14:24, Marek Polacek wrote: > > > > + switch (TREE_CODE (stmt)) > > > > + { > > > > + /* Unfortunately we must handle code like > > > > + false ? bar () : 42 > > > > + where we have to check bar too. */ > > > > + case COND_EXPR: > > > > + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) > > > > + return error_mark_node; > > > > + if (TREE_OPERAND (stmt, 2) > > > > + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) > > > > + return error_mark_node; > > > > > > Is this necessary? Doesn't walk_tree already walk into the arms of > > > COND_EXPR? > > > > Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into > > a constant before we see it here. I've added a comment saying just that. > > Ah. But in that case I guess we need to walk into the arms, not just check > the top-level expression in them. Arg, of course. I was fooled into thinking that it would recurse, but you're right. Fixed by using cp_walk_tree as I intended. Tested in consteval34.C. > But maybe cp_fold_r should do that before the cp_fold, instead of this > function? I...am not sure how that would be better than what I did. > > > > + break; > > > > + > > > > case PTRMEM_CST: > > > > if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL > > > > && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) > > > > { > > > > - if (!data->pset.add (stmt)) > > > > + if (!data->pset.add (stmt) && (complain & tf_error)) > > > > error_at (PTRMEM_CST_LOCATION (stmt), > > > > "taking address of an immediate function %qD", > > > > PTRMEM_CST_MEMBER (stmt)); > > > > stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); > > > > > > It looks like this will overwrite *stmt_p even if we didn't give an error. > > > > I suppose that could result in missing errors, adjusted. And there's no > > point in setting stmt. > > > > - break; > > > > + return error_mark_node; > > > > } > > > > break; > > > > + /* Expand immediate invocations. */ > > > > + case CALL_EXPR: > > > > + case AGGR_INIT_EXPR: > > > > + if (tree fn = cp_get_callee (stmt)) > > > > + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) > > > > + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) > > > > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > > > > + { > > > > + *stmt_p = stmt = cxx_constant_value (stmt, complain); > > > > > > Likewise. > > > > I think we have to keep setting *stmt_p to actually evaluate consteval > > functions. > > But only when it succeeds; we don't want to set it to error_mark_node if we > aren't complaining. Hmm, probably not. Fixed, thanks. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. Since in cxx_* I can't rely on current_function_decl being available, I've added another walk: a new overload for in_immediate_context that looks into constexpr_ctx. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (in_immediate_context): No longer static. (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (in_immediate_context): New overload. (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test. --- gcc/cp/call.cc | 40 +----- gcc/cp/constexpr.cc | 23 +++- gcc/cp/cp-gimplify.cc | 129 ++++++++++++++---- gcc/cp/cp-tree.h | 42 +++--- gcc/cp/tree.cc | 23 +--- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 + gcc/testsuite/g++.dg/cpp2a/consteval11.C | 33 +++-- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 +++++ gcc/testsuite/g++.dg/cpp2a/consteval34.C | 21 +++ gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 ++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 15 files changed, 252 insertions(+), 125 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 399345307ea..1b99967e4cf 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10436,6 +10436,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10453,41 +10457,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 0ca4370deab..a673a6022f1 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (DECL_IMMEDIATE_FUNCTION_P (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) @@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, } /* Subroutine of cxx_eval_constant_expression. - Attempt to evaluate condition expressions. Dead branches are not - looked into. */ + Attempt to evaluate condition expressions. */ static tree cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, @@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, boolean_type_node); } /* Don't VERIFY_CONSTANT the other operands. */ - if (integer_zerop (val)) + const bool zero_p = integer_zerop (val); + if (zero_p) val = TREE_OPERAND (t, 2); else val = TREE_OPERAND (t, 1); if (TREE_CODE (t) == IF_STMT && !val) val = void_node; + + /* P2564: a subexpression of a manifestly constant-evaluated expression + or conversion is an immediate function context. */ + if (ctx->manifestly_const_eval != mce_true + && !in_immediate_context () + && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2), + ctx->manifestly_const_eval)) + { + *non_constant_p = true; + return t; + } + /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still serve as the initializer for the same object as the outer TARGET_EXPR, as in diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..b6671c99e6f 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1000,7 +1000,7 @@ cp_genericize_target_expr (tree *stmt_p) replacement when cp_folding TARGET_EXPR to preserve the invariant that AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ -bool +static bool maybe_replace_decl (tree *tp, tree decl, tree replacement) { if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) @@ -1029,44 +1029,82 @@ struct cp_genericize_data bool handle_invisiref_parm_p; }; -/* Perform any pre-gimplification folding of C++ front end trees to - GENERIC. - Note: The folding of non-omp cases is something to move into - the middle-end. As for now we have most foldings only on GENERIC - in fold-const, we need to perform this before transformation to - GIMPLE-form. */ +/* A subroutine of cp_fold_r to handle immediate functions. */ static tree -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) { - cp_fold_data *data = (cp_fold_data*)data_; + auto data = static_cast<cp_fold_data *>(data_); tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); + const tsubst_flags_t complain = (data->flags == ff_none ? tf_none : tf_error); - switch (code) + /* No need to look into types or unevaluated operands. + NB: This affects cp_fold_r as well. */ + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) + { + *walk_subtrees = 0; + return NULL_TREE; + } + + switch (TREE_CODE (stmt)) { + /* Unfortunately we must handle code like + false ? bar () : 42 + where we have to check bar too. The cp_fold call in cp_fold_r could + fold the ?: into a constant before we see it here. */ + case COND_EXPR: + if (cp_walk_tree (&TREE_OPERAND (stmt, 1), cp_fold_immediate_r, data, + nullptr)) + return error_mark_node; + if (TREE_OPERAND (stmt, 2) + && cp_walk_tree (&TREE_OPERAND (stmt, 2), cp_fold_immediate_r, data, + nullptr)) + return error_mark_node; + break; + case PTRMEM_CST: if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) { - if (!data->pset.add (stmt)) - error_at (PTRMEM_CST_LOCATION (stmt), - "taking address of an immediate function %qD", - PTRMEM_CST_MEMBER (stmt)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (!data->pset.add (stmt) && (complain & tf_error)) + { + error_at (PTRMEM_CST_LOCATION (stmt), + "taking address of an immediate function %qD", + PTRMEM_CST_MEMBER (stmt)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + { + stmt = cxx_constant_value (stmt, complain); + if (stmt == error_mark_node && (complain & tf_error)) + return error_mark_node; + *stmt_p = stmt; + } + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) + && !ADDR_EXPR_DENOTES_CALL_P (stmt)) { - error_at (EXPR_LOCATION (stmt), - "taking address of an immediate function %qD", - TREE_OPERAND (stmt, 0)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (complain & tf_error) + { + error_at (EXPR_LOCATION (stmt), + "taking address of an immediate function %qD", + TREE_OPERAND (stmt, 0)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; @@ -1074,6 +1112,43 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } + return NULL_TREE; +} + +/* A wrapper around cp_fold_immediate_r. Return true if we found + a non-constant immediate function, or taking the address of an + immediate function. */ + +bool +cp_fold_immediate (tree *tp, mce_value manifestly_const_eval) +{ + if (cxx_dialect <= cxx17) + return false; + + fold_flags_t flags = ff_none; + if (manifestly_const_eval == mce_false) + flags |= ff_mce_false; + + cp_fold_data data (flags); + return !!cp_walk_tree (tp, cp_fold_immediate_r, &data, nullptr); +} + +/* Perform any pre-gimplification folding of C++ front end trees to + GENERIC. + Note: The folding of non-omp cases is something to move into + the middle-end. As for now we have most foldings only on GENERIC + in fold-const, we need to perform this before transformation to + GIMPLE-form. */ + +static tree +cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +{ + cp_fold_data *data = (cp_fold_data*)data_; + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + cp_fold_immediate_r (stmt_p, walk_subtrees, data); + *stmt_p = stmt = cp_fold (*stmt_p, data->flags); if (data->pset.add (stmt)) @@ -1084,7 +1159,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) always the same tree, which the first time cp_fold_r has been called on it had the subtrees walked. */ *walk_subtrees = 0; - return NULL; + return NULL_TREE; } code = TREE_CODE (stmt); @@ -1136,7 +1211,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; case IF_STMT: if (IF_STMT_CONSTEVAL_P (stmt)) @@ -1146,7 +1221,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL); cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; } break; @@ -1183,7 +1258,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } - return NULL; + return NULL_TREE; } /* Fold ALL the trees! FIXME we should be able to remove this, but diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..5084932633a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6580,6 +6585,24 @@ extern int class_dump_id; extern int module_dump_id; extern int raw_dump_id; +/* Whether the current context is manifestly constant-evaluated. + Used by the constexpr machinery to control folding of + __builtin_is_constant_evaluated. */ + +enum class mce_value +{ + /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ + mce_unknown = 0, + /* Fold it to true. */ + mce_true = 1, + /* Fold it to false. Primarily used during cp_fold_function and + cp_fully_fold_init. */ + mce_false = -1, +}; +constexpr mce_value mce_unknown = mce_value::mce_unknown; +constexpr mce_value mce_true = mce_value::mce_true; +constexpr mce_value mce_false = mce_value::mce_false; + /* in call.cc */ extern bool check_dtor_name (tree, tree); int magic_varargs_p (tree); @@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); extern tree fold_builtin_source_location (const_tree); extern tree get_source_location_impl_type (); +extern bool cp_fold_immediate (tree *, mce_value); /* in name-lookup.cc */ extern tree strip_using_decl (tree); @@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef { tree result; }; -/* Whether the current context is manifestly constant-evaluated. - Used by the constexpr machinery to control folding of - __builtin_is_constant_evaluated. */ - -enum class mce_value -{ - /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ - mce_unknown = 0, - /* Fold it to true. */ - mce_true = 1, - /* Fold it to false. Primarily used during cp_fold_function and - cp_fully_fold_init. */ - mce_false = -1, -}; -constexpr mce_value mce_unknown = mce_value::mce_unknown; -constexpr mce_value mce_true = mce_value::mce_true; -constexpr mce_value mce_false = mce_value::mce_false; - extern void fini_constexpr (void); extern bool literal_type_p (tree); extern void maybe_save_constexpr_fundef (tree); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..eaf882f8854 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int */*walk_subtrees*/, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..b2c5472b7de 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -58,6 +58,7 @@ baz (int x) return r; } +// This function is not instantiated so NDR. template <typename T> constexpr int qux (int x) @@ -65,7 +66,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..091127eabbf 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -5,21 +5,21 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } -const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } -int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; void foo () { constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } - constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } - const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C new file mode 100644 index 00000000000..643517f930a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++20 } } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int +foo (bool b) +{ + return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" } +} + +static_assert (foo (false) == 2); + +void +g () +{ + __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" } + int a2[sizeof (bar(3))]; + + int a3 = 0 ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" } + a3 += 0 ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval35.C b/gcc/testsuite/g++.dg/cpp2a/consteval35.C new file mode 100644 index 00000000000..59d23ac482b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval35.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++20 } } + +template <typename T, typename F> +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // ok diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..aa75ba37849 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -15,10 +15,11 @@ void qux () int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } } +// This function is not instantiated so NDR. template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 09f27ba86e3..06f1d96d9b7 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -14,6 +14,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: 92456291849fe88303bbcab366f41dcd4a885ad5 -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v5] c++: Move consteval folding to cp_fold_r 2023-09-14 0:02 ` [PATCH v5] " Marek Polacek @ 2023-09-15 18:08 ` Jason Merrill 2023-09-15 20:32 ` [PATCH v6] " Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-15 18:08 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/13/23 20:02, Marek Polacek wrote: > On Wed, Sep 13, 2023 at 05:57:47PM -0400, Jason Merrill wrote: >> On 9/13/23 16:56, Marek Polacek wrote: >>> On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: >>>> On 9/8/23 14:24, Marek Polacek wrote: >>>>> + switch (TREE_CODE (stmt)) >>>>> + { >>>>> + /* Unfortunately we must handle code like >>>>> + false ? bar () : 42 >>>>> + where we have to check bar too. */ >>>>> + case COND_EXPR: >>>>> + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) >>>>> + return error_mark_node; >>>>> + if (TREE_OPERAND (stmt, 2) >>>>> + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) >>>>> + return error_mark_node; >>>> >>>> Is this necessary? Doesn't walk_tree already walk into the arms of >>>> COND_EXPR? >>> >>> Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into >>> a constant before we see it here. I've added a comment saying just that. >> >> Ah. But in that case I guess we need to walk into the arms, not just check >> the top-level expression in them. > > Arg, of course. I was fooled into thinking that it would recurse, but > you're right. Fixed by using cp_walk_tree as I intended. Tested in > consteval34.C. > >> But maybe cp_fold_r should do that before the cp_fold, instead of this >> function? > > I...am not sure how that would be better than what I did. Callers of cp_fold_immediate don't need this because cp_fold_r isn't involved, so it isn't folding anything. cp_fold_r can walk the arms with cp_fold_r and then clear *walk_subtrees to avoid walking the arms again normally. cp_fold_r uses data->pset to avoid walking the same tree twice; cp_fold_immediate_r currently doesn't do anything to avoid that. If cp_fold_immediate_r doesn't itself call cp_walk_tree, cp_fold_immediate can use cp_walk_tree_without_duplicates. >>>>> + break; >>>>> + >>>>> case PTRMEM_CST: >>>>> if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL >>>>> && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) >>>>> { >>>>> - if (!data->pset.add (stmt)) >>>>> + if (!data->pset.add (stmt) && (complain & tf_error)) >>>>> error_at (PTRMEM_CST_LOCATION (stmt), >>>>> "taking address of an immediate function %qD", >>>>> PTRMEM_CST_MEMBER (stmt)); >>>>> stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); >>>> >>>> It looks like this will overwrite *stmt_p even if we didn't give an error. >>> >>> I suppose that could result in missing errors, adjusted. And there's no >>> point in setting stmt. >>>>> - break; >>>>> + return error_mark_node; >>>>> } >>>>> break; >>>>> + /* Expand immediate invocations. */ >>>>> + case CALL_EXPR: >>>>> + case AGGR_INIT_EXPR: >>>>> + if (tree fn = cp_get_callee (stmt)) >>>>> + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) >>>>> + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) >>>>> + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) >>>>> + { >>>>> + *stmt_p = stmt = cxx_constant_value (stmt, complain); >>>> >>>> Likewise. >>> >>> I think we have to keep setting *stmt_p to actually evaluate consteval >>> functions. >> >> But only when it succeeds; we don't want to set it to error_mark_node if we >> aren't complaining. > > Hmm, probably not. Fixed, thanks. > > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > + { > + stmt = cxx_constant_value (stmt, complain); > + if (stmt == error_mark_node && (complain & tf_error)) > + return error_mark_node; > + *stmt_p = stmt; > + } This seems backwards; like with the ADDR_EXPR/PTRMEM_CST cases, I think we want to return error_mark_node regardless of complain, but only set *stmt_p when complaining. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v6] c++: Move consteval folding to cp_fold_r 2023-09-15 18:08 ` Jason Merrill @ 2023-09-15 20:32 ` Marek Polacek 2023-09-16 20:22 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-15 20:32 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Fri, Sep 15, 2023 at 02:08:46PM -0400, Jason Merrill wrote: > On 9/13/23 20:02, Marek Polacek wrote: > > On Wed, Sep 13, 2023 at 05:57:47PM -0400, Jason Merrill wrote: > > > On 9/13/23 16:56, Marek Polacek wrote: > > > > On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: > > > > > On 9/8/23 14:24, Marek Polacek wrote: > > > > > > + switch (TREE_CODE (stmt)) > > > > > > + { > > > > > > + /* Unfortunately we must handle code like > > > > > > + false ? bar () : 42 > > > > > > + where we have to check bar too. */ > > > > > > + case COND_EXPR: > > > > > > + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) > > > > > > + return error_mark_node; > > > > > > + if (TREE_OPERAND (stmt, 2) > > > > > > + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) > > > > > > + return error_mark_node; > > > > > > > > > > Is this necessary? Doesn't walk_tree already walk into the arms of > > > > > COND_EXPR? > > > > > > > > Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into > > > > a constant before we see it here. I've added a comment saying just that. > > > > > > Ah. But in that case I guess we need to walk into the arms, not just check > > > the top-level expression in them. > > Arg, of course. I was fooled into thinking that it would recurse, but > > you're right. Fixed by using cp_walk_tree as I intended. Tested in > > consteval34.C. > > > > > But maybe cp_fold_r should do that before the cp_fold, instead of this > > > function? > > > > I...am not sure how that would be better than what I did. > > Callers of cp_fold_immediate don't need this because cp_fold_r isn't > involved, so it isn't folding anything. This is true. > cp_fold_r can walk the arms with cp_fold_r and then clear *walk_subtrees to > avoid walking the arms again normally. I didn't think we wanted to do everything cp_fold_r does even in dead branches, but ok. I figured that we need to recurse on the conditional of the COND_EXPR too. It can be, for example: &((const struct S *) VIEW_CONVERT_EXPR<const struct S * const>(q))->b != 0B which is supposed to be evaluated into: &((const struct S *) &s)->b != 0B otherwise we'd get ICEs like "error: constant not recomputed when 'ADDR_EXPR' changed". > cp_fold_r uses data->pset to avoid walking the same tree twice; > cp_fold_immediate_r currently doesn't do anything to avoid that. If > cp_fold_immediate_r doesn't itself call cp_walk_tree, cp_fold_immediate can > use cp_walk_tree_without_duplicates. Adjusted. > > > > > > + break; > > > > > > + > > > > > > case PTRMEM_CST: > > > > > > if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL > > > > > > && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) > > > > > > { > > > > > > - if (!data->pset.add (stmt)) > > > > > > + if (!data->pset.add (stmt) && (complain & tf_error)) > > > > > > error_at (PTRMEM_CST_LOCATION (stmt), > > > > > > "taking address of an immediate function %qD", > > > > > > PTRMEM_CST_MEMBER (stmt)); > > > > > > stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); > > > > > > > > > > It looks like this will overwrite *stmt_p even if we didn't give an error. > > > > > > > > I suppose that could result in missing errors, adjusted. And there's no > > > > point in setting stmt. > > > > > > - break; > > > > > > + return error_mark_node; > > > > > > } > > > > > > break; > > > > > > + /* Expand immediate invocations. */ > > > > > > + case CALL_EXPR: > > > > > > + case AGGR_INIT_EXPR: > > > > > > + if (tree fn = cp_get_callee (stmt)) > > > > > > + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) > > > > > > + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) > > > > > > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > > > > > > + { > > > > > > + *stmt_p = stmt = cxx_constant_value (stmt, complain); > > > > > > > > > > Likewise. > > > > > > > > I think we have to keep setting *stmt_p to actually evaluate consteval > > > > functions. > > > > > > But only when it succeeds; we don't want to set it to error_mark_node if we > > > aren't complaining. > > > > Hmm, probably not. Fixed, thanks. > > > > + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) > > + { > > + stmt = cxx_constant_value (stmt, complain); > > + if (stmt == error_mark_node && (complain & tf_error)) > > + return error_mark_node; > > + *stmt_p = stmt; > > + } > > This seems backwards; like with the ADDR_EXPR/PTRMEM_CST cases, I think we > want to return error_mark_node regardless of complain, but only set *stmt_p > when complaining. Ug. I really hope it's alright this time. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? Thanks, -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (in_immediate_context): No longer static. (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (in_immediate_context): New overload. (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. Recurse into a COND_EXPR's operands. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test. --- gcc/cp/call.cc | 40 +----- gcc/cp/constexpr.cc | 23 ++- gcc/cp/cp-gimplify.cc | 132 ++++++++++++++---- gcc/cp/cp-tree.h | 42 +++--- gcc/cp/tree.cc | 23 +-- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 + gcc/testsuite/g++.dg/cpp2a/consteval11.C | 33 +++-- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 +++++ gcc/testsuite/g++.dg/cpp2a/consteval34.C | 21 +++ gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 ++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 15 files changed, 255 insertions(+), 125 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 399345307ea..1b99967e4cf 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10436,6 +10436,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10453,41 +10457,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 0ca4370deab..a673a6022f1 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (DECL_IMMEDIATE_FUNCTION_P (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) @@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, } /* Subroutine of cxx_eval_constant_expression. - Attempt to evaluate condition expressions. Dead branches are not - looked into. */ + Attempt to evaluate condition expressions. */ static tree cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, @@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, boolean_type_node); } /* Don't VERIFY_CONSTANT the other operands. */ - if (integer_zerop (val)) + const bool zero_p = integer_zerop (val); + if (zero_p) val = TREE_OPERAND (t, 2); else val = TREE_OPERAND (t, 1); if (TREE_CODE (t) == IF_STMT && !val) val = void_node; + + /* P2564: a subexpression of a manifestly constant-evaluated expression + or conversion is an immediate function context. */ + if (ctx->manifestly_const_eval != mce_true + && !in_immediate_context () + && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2), + ctx->manifestly_const_eval)) + { + *non_constant_p = true; + return t; + } + /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still serve as the initializer for the same object as the outer TARGET_EXPR, as in diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..874ccc45a02 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1000,7 +1000,7 @@ cp_genericize_target_expr (tree *stmt_p) replacement when cp_folding TARGET_EXPR to preserve the invariant that AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ -bool +static bool maybe_replace_decl (tree *tp, tree decl, tree replacement) { if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) @@ -1029,44 +1029,72 @@ struct cp_genericize_data bool handle_invisiref_parm_p; }; -/* Perform any pre-gimplification folding of C++ front end trees to - GENERIC. - Note: The folding of non-omp cases is something to move into - the middle-end. As for now we have most foldings only on GENERIC - in fold-const, we need to perform this before transformation to - GIMPLE-form. */ +/* A subroutine of cp_fold_r to handle immediate functions. */ static tree -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) { - cp_fold_data *data = (cp_fold_data*)data_; + auto data = static_cast<cp_fold_data *>(data_); tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); + const tsubst_flags_t complain = (data->flags == ff_none ? tf_none : tf_error); - switch (code) + /* No need to look into types or unevaluated operands. + NB: This affects cp_fold_r as well. */ + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) + { + *walk_subtrees = 0; + return NULL_TREE; + } + + switch (TREE_CODE (stmt)) { case PTRMEM_CST: if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) { - if (!data->pset.add (stmt)) - error_at (PTRMEM_CST_LOCATION (stmt), - "taking address of an immediate function %qD", - PTRMEM_CST_MEMBER (stmt)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (!data->pset.add (stmt) && (complain & tf_error)) + { + error_at (PTRMEM_CST_LOCATION (stmt), + "taking address of an immediate function %qD", + PTRMEM_CST_MEMBER (stmt)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + { + stmt = cxx_constant_value (stmt, complain); + if (stmt == error_mark_node) + { + if (complain & tf_error) + *stmt_p = error_mark_node; + return error_mark_node; + } + *stmt_p = stmt; + } + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) + && !ADDR_EXPR_DENOTES_CALL_P (stmt)) { - error_at (EXPR_LOCATION (stmt), - "taking address of an immediate function %qD", - TREE_OPERAND (stmt, 0)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (complain & tf_error) + { + error_at (EXPR_LOCATION (stmt), + "taking address of an immediate function %qD", + TREE_OPERAND (stmt, 0)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; @@ -1074,6 +1102,56 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } + return NULL_TREE; +} + +/* A wrapper around cp_fold_immediate_r. Return true if we found + a non-constant immediate function, or taking the address of an + immediate function. */ + +bool +cp_fold_immediate (tree *tp, mce_value manifestly_const_eval) +{ + if (cxx_dialect <= cxx17) + return false; + + fold_flags_t flags = ff_none; + if (manifestly_const_eval == mce_false) + flags |= ff_mce_false; + + cp_fold_data data (flags); + return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data); +} + +/* Perform any pre-gimplification folding of C++ front end trees to + GENERIC. + Note: The folding of non-omp cases is something to move into + the middle-end. As for now we have most foldings only on GENERIC + in fold-const, we need to perform this before transformation to + GIMPLE-form. */ + +static tree +cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +{ + cp_fold_data *data = (cp_fold_data*)data_; + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + cp_fold_immediate_r (stmt_p, walk_subtrees, data); + + /* Unfortunately we must handle code like + false ? bar () : 42 + where we have to check bar too. The cp_fold call could fold the ?: into + a constant before cp_fold_immediate_r checked it. */ + if (code == COND_EXPR) + { + cp_walk_tree (&TREE_OPERAND (stmt, 0), cp_fold_r, data, nullptr); + cp_walk_tree (&TREE_OPERAND (stmt, 1), cp_fold_r, data, nullptr); + if (TREE_OPERAND (stmt, 2)) + cp_walk_tree (&TREE_OPERAND (stmt, 2), cp_fold_r, data, nullptr); + *walk_subtrees = 0; + } + *stmt_p = stmt = cp_fold (*stmt_p, data->flags); if (data->pset.add (stmt)) @@ -1084,7 +1162,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) always the same tree, which the first time cp_fold_r has been called on it had the subtrees walked. */ *walk_subtrees = 0; - return NULL; + return NULL_TREE; } code = TREE_CODE (stmt); @@ -1136,7 +1214,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; case IF_STMT: if (IF_STMT_CONSTEVAL_P (stmt)) @@ -1146,7 +1224,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL); cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; } break; @@ -1183,7 +1261,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } - return NULL; + return NULL_TREE; } /* Fold ALL the trees! FIXME we should be able to remove this, but diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..5084932633a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6580,6 +6585,24 @@ extern int class_dump_id; extern int module_dump_id; extern int raw_dump_id; +/* Whether the current context is manifestly constant-evaluated. + Used by the constexpr machinery to control folding of + __builtin_is_constant_evaluated. */ + +enum class mce_value +{ + /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ + mce_unknown = 0, + /* Fold it to true. */ + mce_true = 1, + /* Fold it to false. Primarily used during cp_fold_function and + cp_fully_fold_init. */ + mce_false = -1, +}; +constexpr mce_value mce_unknown = mce_value::mce_unknown; +constexpr mce_value mce_true = mce_value::mce_true; +constexpr mce_value mce_false = mce_value::mce_false; + /* in call.cc */ extern bool check_dtor_name (tree, tree); int magic_varargs_p (tree); @@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); extern tree fold_builtin_source_location (const_tree); extern tree get_source_location_impl_type (); +extern bool cp_fold_immediate (tree *, mce_value); /* in name-lookup.cc */ extern tree strip_using_decl (tree); @@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef { tree result; }; -/* Whether the current context is manifestly constant-evaluated. - Used by the constexpr machinery to control folding of - __builtin_is_constant_evaluated. */ - -enum class mce_value -{ - /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ - mce_unknown = 0, - /* Fold it to true. */ - mce_true = 1, - /* Fold it to false. Primarily used during cp_fold_function and - cp_fully_fold_init. */ - mce_false = -1, -}; -constexpr mce_value mce_unknown = mce_value::mce_unknown; -constexpr mce_value mce_true = mce_value::mce_true; -constexpr mce_value mce_false = mce_value::mce_false; - extern void fini_constexpr (void); extern bool literal_type_p (tree); extern void maybe_save_constexpr_fundef (tree); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..eaf882f8854 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int */*walk_subtrees*/, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..b2c5472b7de 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -58,6 +58,7 @@ baz (int x) return r; } +// This function is not instantiated so NDR. template <typename T> constexpr int qux (int x) @@ -65,7 +66,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..091127eabbf 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -5,21 +5,21 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } -const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } -int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; void foo () { constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } - constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } - const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C new file mode 100644 index 00000000000..643517f930a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++20 } } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int +foo (bool b) +{ + return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" } +} + +static_assert (foo (false) == 2); + +void +g () +{ + __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" } + int a2[sizeof (bar(3))]; + + int a3 = 0 ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" } + a3 += 0 ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval35.C b/gcc/testsuite/g++.dg/cpp2a/consteval35.C new file mode 100644 index 00000000000..59d23ac482b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval35.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++20 } } + +template <typename T, typename F> +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // ok diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..aa75ba37849 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -15,10 +15,11 @@ void qux () int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } } +// This function is not instantiated so NDR. template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 09f27ba86e3..06f1d96d9b7 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -14,6 +14,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: fd5a858eb5ef93c4ac7b0399b67d46805d2dabec -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v6] c++: Move consteval folding to cp_fold_r 2023-09-15 20:32 ` [PATCH v6] " Marek Polacek @ 2023-09-16 20:22 ` Jason Merrill 2023-09-18 21:42 ` [PATCH v7] " Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-16 20:22 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/15/23 16:32, Marek Polacek wrote: > On Fri, Sep 15, 2023 at 02:08:46PM -0400, Jason Merrill wrote: >> On 9/13/23 20:02, Marek Polacek wrote: >>> On Wed, Sep 13, 2023 at 05:57:47PM -0400, Jason Merrill wrote: >>>> On 9/13/23 16:56, Marek Polacek wrote: >>>>> On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: >>>>>> On 9/8/23 14:24, Marek Polacek wrote: >>>>>>> + switch (TREE_CODE (stmt)) >>>>>>> + { >>>>>>> + /* Unfortunately we must handle code like >>>>>>> + false ? bar () : 42 >>>>>>> + where we have to check bar too. */ >>>>>>> + case COND_EXPR: >>>>>>> + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) >>>>>>> + return error_mark_node; >>>>>>> + if (TREE_OPERAND (stmt, 2) >>>>>>> + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) >>>>>>> + return error_mark_node; >>>>>> >>>>>> Is this necessary? Doesn't walk_tree already walk into the arms of >>>>>> COND_EXPR? >>>>> >>>>> Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into >>>>> a constant before we see it here. I've added a comment saying just that. >>>> >>>> Ah. But in that case I guess we need to walk into the arms, not just check >>>> the top-level expression in them. >>> Arg, of course. I was fooled into thinking that it would recurse, but >>> you're right. Fixed by using cp_walk_tree as I intended. Tested in >>> consteval34.C. >>> >>>> But maybe cp_fold_r should do that before the cp_fold, instead of this >>>> function? >>> >>> I...am not sure how that would be better than what I did. >> >> Callers of cp_fold_immediate don't need this because cp_fold_r isn't >> involved, so it isn't folding anything. > > This is true. > >> cp_fold_r can walk the arms with cp_fold_r and then clear *walk_subtrees to >> avoid walking the arms again normally. > > I didn't think we wanted to do everything cp_fold_r does even in dead > branches, but ok. Ah, that's a good point. With the recursive walk in cp_fold_immediate_r, I suppose we could suppress it when called from cp_fold_immediate with a new fold_flag? That would still allow for cp_walk_tree_without_duplicates. Incidentally, I notice you check for null op2 of COND_EXPR, should probably also check op1. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v7] c++: Move consteval folding to cp_fold_r 2023-09-16 20:22 ` Jason Merrill @ 2023-09-18 21:42 ` Marek Polacek 2023-09-19 1:36 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-18 21:42 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Sat, Sep 16, 2023 at 04:22:44PM -0400, Jason Merrill wrote: > On 9/15/23 16:32, Marek Polacek wrote: > > On Fri, Sep 15, 2023 at 02:08:46PM -0400, Jason Merrill wrote: > > > On 9/13/23 20:02, Marek Polacek wrote: > > > > On Wed, Sep 13, 2023 at 05:57:47PM -0400, Jason Merrill wrote: > > > > > On 9/13/23 16:56, Marek Polacek wrote: > > > > > > On Tue, Sep 12, 2023 at 05:26:25PM -0400, Jason Merrill wrote: > > > > > > > On 9/8/23 14:24, Marek Polacek wrote: > > > > > > > > + switch (TREE_CODE (stmt)) > > > > > > > > + { > > > > > > > > + /* Unfortunately we must handle code like > > > > > > > > + false ? bar () : 42 > > > > > > > > + where we have to check bar too. */ > > > > > > > > + case COND_EXPR: > > > > > > > > + if (cp_fold_immediate_r (&TREE_OPERAND (stmt, 1), walk_subtrees, data)) > > > > > > > > + return error_mark_node; > > > > > > > > + if (TREE_OPERAND (stmt, 2) > > > > > > > > + && cp_fold_immediate_r (&TREE_OPERAND (stmt, 2), walk_subtrees, data)) > > > > > > > > + return error_mark_node; > > > > > > > > > > > > > > Is this necessary? Doesn't walk_tree already walk into the arms of > > > > > > > COND_EXPR? > > > > > > > > > > > > Unfortunately yes. The cp_fold call in cp_fold_r could fold the ?: into > > > > > > a constant before we see it here. I've added a comment saying just that. > > > > > > > > > > Ah. But in that case I guess we need to walk into the arms, not just check > > > > > the top-level expression in them. > > > > Arg, of course. I was fooled into thinking that it would recurse, but > > > > you're right. Fixed by using cp_walk_tree as I intended. Tested in > > > > consteval34.C. > > > > > > > > > But maybe cp_fold_r should do that before the cp_fold, instead of this > > > > > function? > > > > > > > > I...am not sure how that would be better than what I did. > > > > > > Callers of cp_fold_immediate don't need this because cp_fold_r isn't > > > involved, so it isn't folding anything. > > > > This is true. > > > cp_fold_r can walk the arms with cp_fold_r and then clear *walk_subtrees to > > > avoid walking the arms again normally. > > > > I didn't think we wanted to do everything cp_fold_r does even in dead > > branches, but ok. > > Ah, that's a good point. With the recursive walk in cp_fold_immediate_r, I > suppose we could suppress it when called from cp_fold_immediate with a new > fold_flag? That would still allow for cp_walk_tree_without_duplicates. Yeah, I like that. Done here. > Incidentally, I notice you check for null op2 of COND_EXPR, should probably > also check op1. Added, though I'm not sure it's necessary. I've added some more tests to consteval34.C to also check for a ?: c. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (enum fold_flags): Add ff_fold_immediate. (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test. --- gcc/cp/call.cc | 40 +---- gcc/cp/constexpr.cc | 23 ++- gcc/cp/cp-gimplify.cc | 144 ++++++++++++++---- gcc/cp/cp-tree.h | 42 ++--- gcc/cp/tree.cc | 23 +-- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 7 + gcc/testsuite/g++.dg/cpp2a/consteval11.C | 33 ++-- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 3 +- gcc/testsuite/g++.dg/cpp2a/consteval32.C | 4 + gcc/testsuite/g++.dg/cpp2a/consteval33.C | 34 +++++ gcc/testsuite/g++.dg/cpp2a/consteval34.C | 33 ++++ gcc/testsuite/g++.dg/cpp2a/consteval35.C | 10 ++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 3 +- .../testsuite/20_util/allocator/105975.cc | 2 +- 15 files changed, 279 insertions(+), 125 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval32.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval33.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval34.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval35.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 2bbaeee039d..e8dafbd8ba6 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10434,6 +10434,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) fn = build_addr_func (fn, complain); if (fn == error_mark_node) return error_mark_node; + + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); @@ -10451,41 +10455,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) suppress_warning (c /* Suppress all warnings. */); } - if (TREE_CODE (fn) == ADDR_EXPR) - { - tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (immediate_invocation_p (fndecl)) - { - tree obj_arg = NULL_TREE; - /* Undo convert_from_reference called by build_cxx_call. */ - if (REFERENCE_REF_P (call)) - call = TREE_OPERAND (call, 0); - if (DECL_CONSTRUCTOR_P (fndecl)) - obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; - if (obj_arg && is_dummy_object (obj_arg)) - { - call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); - obj_arg = NULL_TREE; - } - /* Look through *(const T *)&obj. */ - else if (obj_arg && INDIRECT_REF_P (obj_arg)) - { - tree addr = TREE_OPERAND (obj_arg, 0); - STRIP_NOPS (addr); - if (TREE_CODE (addr) == ADDR_EXPR) - { - tree typeo = TREE_TYPE (obj_arg); - tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); - if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) - obj_arg = TREE_OPERAND (addr, 0); - } - } - call = cxx_constant_value (call, obj_arg, complain); - if (obj_arg && !error_operand_p (call)) - call = cp_build_init_expr (obj_arg, call); - call = convert_from_reference (call); - } - } + return call; } diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 0ca4370deab..a673a6022f1 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; + /* Make sure we fold std::is_constant_evaluated to true in an + immediate function. */ + if (DECL_IMMEDIATE_FUNCTION_P (fun)) + call_ctx.manifestly_const_eval = mce_true; + /* If this is a constexpr destructor, the object's const and volatile semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) @@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, } /* Subroutine of cxx_eval_constant_expression. - Attempt to evaluate condition expressions. Dead branches are not - looked into. */ + Attempt to evaluate condition expressions. */ static tree cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, @@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, boolean_type_node); } /* Don't VERIFY_CONSTANT the other operands. */ - if (integer_zerop (val)) + const bool zero_p = integer_zerop (val); + if (zero_p) val = TREE_OPERAND (t, 2); else val = TREE_OPERAND (t, 1); if (TREE_CODE (t) == IF_STMT && !val) val = void_node; + + /* P2564: a subexpression of a manifestly constant-evaluated expression + or conversion is an immediate function context. */ + if (ctx->manifestly_const_eval != mce_true + && !in_immediate_context () + && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2), + ctx->manifestly_const_eval)) + { + *non_constant_p = true; + return t; + } + /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still serve as the initializer for the same object as the outer TARGET_EXPR, as in diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..78dbc9bc4ff 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -53,6 +53,8 @@ enum fold_flags { definitely not in a manifestly constant-evaluated context. */ ff_mce_false = 1 << 1, + /* Whether we're being called from cp_fold_immediate. */ + ff_fold_immediate = 1 << 2, }; using fold_flags_t = int; @@ -1000,7 +1002,7 @@ cp_genericize_target_expr (tree *stmt_p) replacement when cp_folding TARGET_EXPR to preserve the invariant that AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */ -bool +static bool maybe_replace_decl (tree *tp, tree decl, tree replacement) { if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp))) @@ -1029,44 +1031,95 @@ struct cp_genericize_data bool handle_invisiref_parm_p; }; -/* Perform any pre-gimplification folding of C++ front end trees to - GENERIC. - Note: The folding of non-omp cases is something to move into - the middle-end. As for now we have most foldings only on GENERIC - in fold-const, we need to perform this before transformation to - GIMPLE-form. */ +/* A subroutine of cp_fold_r to handle immediate functions. */ static tree -cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_) { - cp_fold_data *data = (cp_fold_data*)data_; + auto data = static_cast<cp_fold_data *>(data_); tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); + /* The purpose of this is not to emit errors for mce_unknown. */ + const tsubst_flags_t complain = (data->flags == ff_fold_immediate + ? tf_none : tf_error); - switch (code) + /* No need to look into types or unevaluated operands. + NB: This affects cp_fold_r as well. */ + if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt))) + { + *walk_subtrees = 0; + return NULL_TREE; + } + + switch (TREE_CODE (stmt)) { + /* Unfortunately we must handle code like + false ? bar () : 42 + where we have to check bar too. The cp_fold call in cp_fold_r could + fold the ?: into a constant before we see it here. */ + case COND_EXPR: + /* If we are called from cp_fold_immediate, we don't need to worry about + cp_fold folding away the COND_EXPR. */ + if (data->flags & ff_fold_immediate) + break; + if (TREE_OPERAND (stmt, 1) + && cp_walk_tree (&TREE_OPERAND (stmt, 1), cp_fold_immediate_r, data, + nullptr)) + return error_mark_node; + if (TREE_OPERAND (stmt, 2) + && cp_walk_tree (&TREE_OPERAND (stmt, 2), cp_fold_immediate_r, data, + nullptr)) + return error_mark_node; + /* We're done here. Don't clear *walk_subtrees here though: we're called + from cp_fold_r and we must let it recurse on the expression with + cp_fold. */ + break; case PTRMEM_CST: if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) { - if (!data->pset.add (stmt)) - error_at (PTRMEM_CST_LOCATION (stmt), - "taking address of an immediate function %qD", - PTRMEM_CST_MEMBER (stmt)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (!data->pset.add (stmt) && (complain & tf_error)) + { + error_at (PTRMEM_CST_LOCATION (stmt), + "taking address of an immediate function %qD", + PTRMEM_CST_MEMBER (stmt)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; + /* Expand immediate invocations. */ + case CALL_EXPR: + case AGGR_INIT_EXPR: + if (tree fn = cp_get_callee (stmt)) + if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn)) + if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + { + stmt = cxx_constant_value (stmt, complain); + if (stmt == error_mark_node) + { + if (complain & tf_error) + *stmt_p = error_mark_node; + return error_mark_node; + } + *stmt_p = stmt; + } + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) + && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)) + && !ADDR_EXPR_DENOTES_CALL_P (stmt)) { - error_at (EXPR_LOCATION (stmt), - "taking address of an immediate function %qD", - TREE_OPERAND (stmt, 0)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + if (complain & tf_error) + { + error_at (EXPR_LOCATION (stmt), + "taking address of an immediate function %qD", + TREE_OPERAND (stmt, 0)); + *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } + return error_mark_node; } break; @@ -1074,6 +1127,43 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } + return NULL_TREE; +} + +/* A wrapper around cp_fold_immediate_r. Return true if we found + a non-constant immediate function, or taking the address of an + immediate function. */ + +bool +cp_fold_immediate (tree *tp, mce_value manifestly_const_eval) +{ + if (cxx_dialect <= cxx17) + return false; + + fold_flags_t flags = ff_fold_immediate; + if (manifestly_const_eval == mce_false) + flags |= ff_mce_false; + + cp_fold_data data (flags); + return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data); +} + +/* Perform any pre-gimplification folding of C++ front end trees to + GENERIC. + Note: The folding of non-omp cases is something to move into + the middle-end. As for now we have most foldings only on GENERIC + in fold-const, we need to perform this before transformation to + GIMPLE-form. */ + +static tree +cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) +{ + cp_fold_data *data = (cp_fold_data*)data_; + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + + cp_fold_immediate_r (stmt_p, walk_subtrees, data); + *stmt_p = stmt = cp_fold (*stmt_p, data->flags); if (data->pset.add (stmt)) @@ -1084,7 +1174,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) always the same tree, which the first time cp_fold_r has been called on it had the subtrees walked. */ *walk_subtrees = 0; - return NULL; + return NULL_TREE; } code = TREE_CODE (stmt); @@ -1136,7 +1226,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) } cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; case IF_STMT: if (IF_STMT_CONSTEVAL_P (stmt)) @@ -1146,7 +1236,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL); cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - return NULL; + return NULL_TREE; } break; @@ -1183,7 +1273,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; } - return NULL; + return NULL_TREE; } /* Fold ALL the trees! FIXME we should be able to remove this, but diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3ca011c61c8..5084932633a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6580,6 +6585,24 @@ extern int class_dump_id; extern int module_dump_id; extern int raw_dump_id; +/* Whether the current context is manifestly constant-evaluated. + Used by the constexpr machinery to control folding of + __builtin_is_constant_evaluated. */ + +enum class mce_value +{ + /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ + mce_unknown = 0, + /* Fold it to true. */ + mce_true = 1, + /* Fold it to false. Primarily used during cp_fold_function and + cp_fully_fold_init. */ + mce_false = -1, +}; +constexpr mce_value mce_unknown = mce_value::mce_unknown; +constexpr mce_value mce_true = mce_value::mce_true; +constexpr mce_value mce_false = mce_value::mce_false; + /* in call.cc */ extern bool check_dtor_name (tree, tree); int magic_varargs_p (tree); @@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); extern tree fold_builtin_source_location (const_tree); extern tree get_source_location_impl_type (); +extern bool cp_fold_immediate (tree *, mce_value); /* in name-lookup.cc */ extern tree strip_using_decl (tree); @@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef { tree result; }; -/* Whether the current context is manifestly constant-evaluated. - Used by the constexpr machinery to control folding of - __builtin_is_constant_evaluated. */ - -enum class mce_value -{ - /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */ - mce_unknown = 0, - /* Fold it to true. */ - mce_true = 1, - /* Fold it to false. Primarily used during cp_fold_function and - cp_fully_fold_init. */ - mce_false = -1, -}; -constexpr mce_value mce_unknown = mce_value::mce_unknown; -constexpr mce_value mce_true = mce_value::mce_true; -constexpr mce_value mce_false = mce_value::mce_false; - extern void fini_constexpr (void); extern bool literal_type_p (tree); extern void maybe_save_constexpr_fundef (tree); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 799183dc646..eaf882f8854 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) variables. */ static tree -bot_replace (tree* t, int* walk_subtrees, void* data_) +bot_replace (tree* t, int */*walk_subtrees*/, void* data_) { bot_data &data = *(bot_data*)data_; splay_tree target_remap = data.target_remap; @@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_) /*check_access=*/false, /*nonnull=*/true, tf_warning_or_error); } - else if (cxx_dialect >= cxx20 - && (TREE_CODE (*t) == CALL_EXPR - || TREE_CODE (*t) == AGGR_INIT_EXPR) - && !in_immediate_context ()) - { - /* Expand immediate invocations. */ - if (tree fndecl = cp_get_callee_fndecl_nofold (*t)) - if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) - { - /* Make in_immediate_context true within the args. */ - in_consteval_if_p_temp_override ito; - in_consteval_if_p = true; - int nargs = call_expr_nargs (*t); - for (int i = 0; i < nargs; ++i) - cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL); - *t = cxx_constant_value (*t); - if (*t == error_mark_node) - return error_mark_node; - *walk_subtrees = 0; - } - } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..b2c5472b7de 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -58,6 +58,7 @@ baz (int x) return r; } +// This function is not instantiated so NDR. template <typename T> constexpr int qux (int x) @@ -65,7 +66,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } } } else { diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index 910e7a1ac1e..63f4f1d526a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) { fixed_string::size_static(-1); // { dg-message "expansion of" } s(); // { dg-bogus "" } } + +void +do_test () +{ + fixed_string f; + VerifyHash<int>(f); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C index 2f68ec0f892..091127eabbf 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -5,21 +5,21 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } -constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } -const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } -int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; void foo () { constexpr int a = bar (1); constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } - constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } - const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; int f = bar (1); int g = bar (6); // { dg-message "in 'constexpr' expansion of" } int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } @@ -33,13 +33,13 @@ foo () else bar (12); // { dg-message "in 'constexpr' expansion of" } if constexpr (0) - bar (13); // { dg-message "in 'constexpr' expansion of" } + bar (13); else bar (14); // { dg-message "in 'constexpr' expansion of" } if constexpr (1) bar (15); // { dg-message "in 'constexpr' expansion of" } else - bar (16); // { dg-message "in 'constexpr' expansion of" } + bar (16); } consteval int @@ -77,22 +77,25 @@ template <typename T> void qux () { + // Used to give errors errors here, but not since we moved consteval + // function folding to cp_fold_r which isn't called on uninstantiated + // templates. if (0) - bar (2); // { dg-message "in 'constexpr' expansion of" } + bar (2); else - bar (3); // { dg-message "in 'constexpr' expansion of" } + bar (3); if (1) - bar (4); // { dg-message "in 'constexpr' expansion of" } + bar (4); else - bar (5); // { dg-message "in 'constexpr' expansion of" } + bar (5); if constexpr (0) - bar (6); // { dg-message "in 'constexpr' expansion of" } + bar (6); else - bar (7); // { dg-message "in 'constexpr' expansion of" } + bar (7); if constexpr (1) - bar (8); // { dg-message "in 'constexpr' expansion of" } + bar (8); else - bar (9); // { dg-message "in 'constexpr' expansion of" } + bar (9); if (0) bar ((T) 2); else diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 627ab142d5a..9efac8c8eae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; } int d = 6; // { dg-message "'int d' is not const" } int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } -constexpr int f = f7 (5); // { dg-error "" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr int f = f7 (5); using fnptr = int (int); fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } int f8 (fnptr *); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval32.C b/gcc/testsuite/g++.dg/cpp2a/consteval32.C new file mode 100644 index 00000000000..f1de63e41b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval32.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval33.C b/gcc/testsuite/g++.dg/cpp2a/consteval33.C new file mode 100644 index 00000000000..3d50b00c7a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval33.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +consteval int id (int i) { return i; } +consteval int add (int i, int j) { return i + j; } + +constexpr int +foo (int i = id (42)) +{ + return i + id (id (id (0))); +} + +constexpr int +bar (int i = id (id (id (42)))) +{ + return i; +} + +constexpr int +baz (int i = add (add (id (1), id (2)), id (3))) +{ + return i; +} + +void +g () +{ + foo (); + bar (); + baz (); +} + +static_assert (foo () == 42); +static_assert (bar () == 42); +static_assert (baz () == 6); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C new file mode 100644 index 00000000000..d5e2d1dc5d2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -0,0 +1,33 @@ +// { dg-do compile { target c++20 } } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int +foo (bool b) +{ + return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" } +} + +static_assert (foo (false) == 2); + +__extension__ constexpr int g1 = false ?: bar (2); // { dg-message "in .constexpr. expansion" } +__extension__ constexpr int g2 = false ?: (1 + bar (2)); // { dg-message "in .constexpr. expansion" } +__extension__ constexpr int g3 = true ?: bar (2); +__extension__ constexpr int g4 = true ?: (1 + bar (2)); +constexpr int g5 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" } +constexpr int g6 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" } + +void +g () +{ + __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" } + int a2[sizeof (bar(3))]; + + int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" } + a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" } + + __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" } + __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" } + int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" } + int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval35.C b/gcc/testsuite/g++.dg/cpp2a/consteval35.C new file mode 100644 index 00000000000..59d23ac482b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval35.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++20 } } + +template <typename T, typename F> +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // ok diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C index 489286a12d2..aa75ba37849 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -15,10 +15,11 @@ void qux () int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } } +// This function is not instantiated so NDR. template <int N> void quux () { - int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } + int a = bar (5); } void diff --git a/libstdc++-v3/testsuite/20_util/allocator/105975.cc b/libstdc++-v3/testsuite/20_util/allocator/105975.cc index 4866ae6baf9..86e85542342 100644 --- a/libstdc++-v3/testsuite/20_util/allocator/105975.cc +++ b/libstdc++-v3/testsuite/20_util/allocator/105975.cc @@ -13,6 +13,6 @@ consteval bool test_pr105957() a.deallocate(p, n); return true; } -static_assert( test_pr105957() ); +static_assert( test_pr105957() ); // { dg-error "non-constant" } // { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 } base-commit: 80968d5f4683ffb50dbe8051d10f754d5fd00dfb -- 2.41.0 ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7] c++: Move consteval folding to cp_fold_r 2023-09-18 21:42 ` [PATCH v7] " Marek Polacek @ 2023-09-19 1:36 ` Jason Merrill 2023-09-19 13:01 ` Marek Polacek 0 siblings, 1 reply; 19+ messages in thread From: Jason Merrill @ 2023-09-19 1:36 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/18/23 17:42, Marek Polacek wrote: > + /* The purpose of this is not to emit errors for mce_unknown. */ > + const tsubst_flags_t complain = (data->flags == ff_fold_immediate > + ? tf_none : tf_error); Maybe check flags & ff_mce_false, instead? OK with that change. Thanks, Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7] c++: Move consteval folding to cp_fold_r 2023-09-19 1:36 ` Jason Merrill @ 2023-09-19 13:01 ` Marek Polacek 2023-09-19 13:20 ` Jason Merrill 0 siblings, 1 reply; 19+ messages in thread From: Marek Polacek @ 2023-09-19 13:01 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Mon, Sep 18, 2023 at 09:36:31PM -0400, Jason Merrill wrote: > On 9/18/23 17:42, Marek Polacek wrote: > > + /* The purpose of this is not to emit errors for mce_unknown. */ > > + const tsubst_flags_t complain = (data->flags == ff_fold_immediate > > + ? tf_none : tf_error); > > Maybe check flags & ff_mce_false, instead? OK with that change. Thanks! And what do you think about: --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1162,7 +1162,8 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) tree stmt = *stmt_p; enum tree_code code = TREE_CODE (stmt); - cp_fold_immediate_r (stmt_p, walk_subtrees, data); + if (cxx_dialect > cxx17) + cp_fold_immediate_r (stmt_p, walk_subtrees, data); *stmt_p = stmt = cp_fold (*stmt_p, data->flags); since we can recurse on ?:, this could save some time. It's sad that it checks cxx_dialect in every invocation of cp_fold_r but still, it should help. Thanks again for the reviews, Marek ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v7] c++: Move consteval folding to cp_fold_r 2023-09-19 13:01 ` Marek Polacek @ 2023-09-19 13:20 ` Jason Merrill 0 siblings, 0 replies; 19+ messages in thread From: Jason Merrill @ 2023-09-19 13:20 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 9/19/23 09:01, Marek Polacek wrote: > On Mon, Sep 18, 2023 at 09:36:31PM -0400, Jason Merrill wrote: >> On 9/18/23 17:42, Marek Polacek wrote: >>> + /* The purpose of this is not to emit errors for mce_unknown. */ >>> + const tsubst_flags_t complain = (data->flags == ff_fold_immediate >>> + ? tf_none : tf_error); >> >> Maybe check flags & ff_mce_false, instead? OK with that change. > > Thanks! And what do you think about: > > --- a/gcc/cp/cp-gimplify.cc > +++ b/gcc/cp/cp-gimplify.cc > @@ -1162,7 +1162,8 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) > tree stmt = *stmt_p; > enum tree_code code = TREE_CODE (stmt); > > - cp_fold_immediate_r (stmt_p, walk_subtrees, data); > + if (cxx_dialect > cxx17) > + cp_fold_immediate_r (stmt_p, walk_subtrees, data); > > *stmt_p = stmt = cp_fold (*stmt_p, data->flags); > > since we can recurse on ?:, this could save some time. It's sad that > it checks cxx_dialect in every invocation of cp_fold_r but still, it > should help. Sure. Jason ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2023-09-19 13:20 UTC | newest] Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-09-01 17:23 [PATCH] c++: Move consteval folding to cp_fold_r Marek Polacek 2023-09-01 17:36 ` Marek Polacek 2023-09-05 14:52 ` Jason Merrill 2023-09-05 19:59 ` Marek Polacek 2023-09-05 20:36 ` Jason Merrill 2023-09-07 15:23 ` [PATCH v2] " Marek Polacek 2023-09-07 18:32 ` Jason Merrill 2023-09-08 18:24 ` [PATCH v3] " Marek Polacek 2023-09-12 21:26 ` Jason Merrill 2023-09-13 20:56 ` [PATCH v4] " Marek Polacek 2023-09-13 21:57 ` Jason Merrill 2023-09-14 0:02 ` [PATCH v5] " Marek Polacek 2023-09-15 18:08 ` Jason Merrill 2023-09-15 20:32 ` [PATCH v6] " Marek Polacek 2023-09-16 20:22 ` Jason Merrill 2023-09-18 21:42 ` [PATCH v7] " Marek Polacek 2023-09-19 1:36 ` Jason Merrill 2023-09-19 13:01 ` Marek Polacek 2023-09-19 13:20 ` Jason Merrill
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).