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

Reply via email to