* [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).