public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [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).