On 7/10/26 4:28 PM, Marek Polacek wrote:
On Thu, Jul 09, 2026 at 08:43:42PM -0400, Jason Merrill wrote:
On 7/9/26 5:50 PM, Marek Polacek wrote:
On Wed, Jul 08, 2026 at 08:59:32AM -0400, Jason Merrill wrote:
On 7/7/26 6:16 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/16?

-- >8 --
We trip on the assert in lvalue_kind/MODOP_EXPR whose comment says
that we expect to see MODOP_EXPRs only during template processing.
In this test we get there with processing_template_decl==0.  The
MODOP_EXPR is created in:

       /* Parse the requirement body. */
       ++processing_template_decl;
       reqs = cp_parser_requirement_body (parser);
       --processing_template_decl;

but we're not in a template when calling maybe_convert_cond which
calls verify_sequence_points which ends up calling lvalue_p on
the MODOP_EXPR.  verify_sequence_points is a c-family/ function
so we couldn't make it stop recursing on REQUIRES_EXPR, so I suppose
we can do the following.

Eh, this just looks like a workaround for a single testcase.

I see several problems here:

1) We're retaining template trees in a non-template function,
2) verify_sequence_points is walking the unevaluated operand of a requires
because verify_tree assumes that all unknown expressions are evaluated, and
3) REQURES_EXPR looks like a normal expression (tcc_expression).

I think the right way to approach this would be to attack 3 by changing
REQUIRES_EXPR to tcc_exceptional.  Then 1 doesn't matter because the generic
code (such as verify_tree) sees that it's magic and doesn't try to walk into
it.

I did this.  It's no longer a trivial patch but it does fix the crash.

Thanks.

We could also attack 2 by defining unevaluated_p in the C front-end as well,
and checking it in verify_tree.  2 also affects other unevaluated codes like
NOEXCEPT_EXPR, potentially leading to wrong -Wsequence-point results.

I did not do this.  If you think I should, I will.

Maybe just add a FIXME next to the SIZEOF_EXPR case?

Done.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
We trip on the assert in lvalue_kind/MODOP_EXPR whose comment says
that we expect to see MODOP_EXPRs only during template processing.
In this test we get there with processing_template_decl==0.  The
MODOP_EXPR is created in:

      /* Parse the requirement body. */
      ++processing_template_decl;
      reqs = cp_parser_requirement_body (parser);
      --processing_template_decl;

but we're not in a template when calling maybe_convert_cond which
calls verify_sequence_points which ends up calling lvalue_p on
the MODOP_EXPR.

verify_sequence_points is walking the unevaluated operand of a requires
because verify_tree assumes that all unknown expressions are evaluated, and
REQURES_EXPR looks like a normal expression (tcc_expression).

missing I

This patch changes REQURES_EXPR to tcc_exceptional.  That means that

likewise

Both fixed.

the generic code (such as verify_tree) sees that it's magic and doesn't
try to walk into it.

@@ -6530,8 +6530,11 @@ trees_out::core_vals (tree t)
     if (CODE_CONTAINS_STRUCT (code, TS_EXP))
       {
+      location_t loc = (code == REQUIRES_EXPR

Hmm, why does REQUIRES_EXPR have TS_EXP after this patch?

I think you want to remove the MARK_TS_EXP from cp_common_init_ts and then
move REQUIRES_EXPR out of this if.

This is so that I can set TREE_TYPE on a REQUIRES_EXPR.  Code like
maybe_convert_cond needs a tree with a TREE_TYPE.

...but then we should use MARK_TS_TYPED instead.  Fixed.

@@ -7128,7 +7133,10 @@ trees_in::core_vals (tree t)
     if (CODE_CONTAINS_STRUCT (code, TS_EXP))

Likewise.

Done.

@@ -4527,6 +4528,18 @@ cp_tree_equal (tree t1, tree t2)
       case REFLECT_EXPR:
         return compare_reflections (t1, t2);
+    case REQUIRES_EXPR:
+      if (!cp_tree_equal (REQUIRES_EXPR_PARMS (t1),
+                         REQUIRES_EXPR_PARMS (t2)))
+       return false;
+      if (!cp_tree_equal (REQUIRES_EXPR_REQS (t1),
+                         REQUIRES_EXPR_REQS (t2)))
+       return false;
+      if (!cp_tree_equal (REQUIRES_EXPR_EXTRA_ARGS (t1),
+                         REQUIRES_EXPR_EXTRA_ARGS (t2)))
+       return false;
+      return true;

This could be return (x && y && z) but up to you.

I've used that style; it's a bit shorter.  Thanks,

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK, thanks.

For 16, maybe we can just remove the assert.

Or change it to _checking_.

Jason

-- >8 --
We trip on the assert in lvalue_kind/MODOP_EXPR whose comment says
that we expect to see MODOP_EXPRs only during template processing.
In this test we get there with processing_template_decl==0.  The
MODOP_EXPR is created in:

     /* Parse the requirement body. */
     ++processing_template_decl;
     reqs = cp_parser_requirement_body (parser);
     --processing_template_decl;

but we're not in a template when calling maybe_convert_cond which
calls verify_sequence_points which ends up calling lvalue_p on
the MODOP_EXPR.

verify_sequence_points is walking the unevaluated operand of a requires
because verify_tree assumes that all unknown expressions are evaluated, and
REQUIRES_EXPR looks like a normal expression (tcc_expression).

This patch changes REQUIRES_EXPR to tcc_exceptional.  That means that
the generic code (such as verify_tree) sees that it's magic and doesn't
try to walk into it.

        PR c++/126066

gcc/c-family/ChangeLog:

        * c-common.cc (verify_tree): Add a comment.

gcc/cp/ChangeLog:

        * constraint.cc (tsubst_requires_expr): Use
        REQUIRES_EXPR_LOCATION.
        (satisfaction_cache::get): Use cp_expr_location.
        (satisfaction_cache::save): Likewise.
        (finish_requires_expr): Use make_node and set REQUIRES_EXPR_*.
        * cp-objcp-common.cc (cp_tree_size): Handle REQUIRES_EXPR.
        (cp_common_init_ts): Change REQUIRES_EXPR from MARK_TS_EXP to
        MARK_TS_TYPED.
        * cp-tree.def (REQUIRES_EXPR): Make it tcc_exceptional with no
        operands.
        * cp-tree.h (struct tree_requires_expr): New.
        (REQUIRES_EXPR_PARMS): Adjust.
        (REQUIRES_EXPR_REQS): Likewise.
        (REQUIRES_EXPR_EXTRA_ARGS): Likewise.
        (REQUIRES_EXPR_LOCATION): New.
        (enum cp_tree_node_structure_enum): Add TS_CP_REQUIRES_EXPR.
        (union lang_tree_node): Add tree_requires_expr.
        (cp_expr_location): Handle REQUIRES_EXPR.
        * cxx-pretty-print.cc (pp_cxx_requires_expr): Use
        REQUIRES_EXPR_REQS.
        * decl.cc (cp_tree_node_structure): Handle REQUIRES_EXPR.
        * error.cc (print_requires_expression_info): Use
        REQUIRES_EXPR_PARMS.
        * mangle.cc (write_tparms_constraints): Use cp_expr_location.
        * module.cc (trees_out::core_vals): Handle REQUIRES_EXPR
        specially.
        (trees_in::core_vals): Likewise.
        * pt.cc (iterative_hash_template_arg): Handle REQUIRES_EXPR.
        * tree.cc (strip_typedefs_expr): Likewise.
        (cp_tree_equal): Likewise.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/concepts-pr126066.C: New test.
---
  gcc/c-family/c-common.cc                      |  1 +
  gcc/cp/constraint.cc                          | 14 +++++---
  gcc/cp/cp-objcp-common.cc                     |  3 +-
  gcc/cp/cp-tree.def                            | 10 +++---
  gcc/cp/cp-tree.h                              | 27 +++++++++++---
  gcc/cp/cxx-pretty-print.cc                    |  2 +-
  gcc/cp/decl.cc                                |  1 +
  gcc/cp/error.cc                               |  2 +-
  gcc/cp/mangle.cc                              |  6 ++--
  gcc/cp/module.cc                              | 36 ++++++++++++-------
  gcc/cp/pt.cc                                  |  5 +++
  gcc/cp/tree.cc                                | 11 ++++++
  .../g++.dg/cpp2a/concepts-pr126066.C          | 15 ++++++++
  13 files changed, 99 insertions(+), 34 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr126066.C

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index c298ff11a83..a0debcc4976 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -2092,6 +2092,7 @@ verify_tree (tree x, struct tlist **pbefore_sp, struct 
tlist **pno_sp,
      case CONSTRUCTOR:
      case SIZEOF_EXPR:
      case PAREN_SIZEOF_EXPR:
+    /* FIXME: Add C++ codes like NOEXCEPT_EXPR (cf. unevaluated_p) as well.  */
        return;
case COMPOUND_EXPR:
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c4118788d4c..08439293c64 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1777,7 +1777,8 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
        result = tree_cons (NULL_TREE, req, result);
      }
    if (processing_template_decl && result != boolean_false_node)
-    result = finish_requires_expr (EXPR_LOCATION (t), parms, nreverse 
(result));
+    result = finish_requires_expr (REQUIRES_EXPR_LOCATION (t), parms,
+                                  nreverse (result));
    return result;
  }
@@ -2178,7 +2179,7 @@ satisfaction_cache::get ()
        /* Prefer printing the instantiated mapping.  */
        tree atom = entry->inst_entry ? entry->inst_entry->atom : entry->atom;
        if (info.noisy ())
-       error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (atom)),
+       error_at (cp_expr_location (ATOMIC_CONSTR_EXPR (atom)),
                  "satisfaction of atomic constraint %qE depends on itself",
                  atom);
        return error_mark_node;
@@ -2224,7 +2225,7 @@ satisfaction_cache::save (tree result)
          if (entry->diagnose_instability)
            {
              auto_diagnostic_group d;
-             error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)),
+             error_at (cp_expr_location (ATOMIC_CONSTR_EXPR (entry->atom)),
                        "satisfaction value of atomic constraint %qE changed "
                        "from %qE to %qE", entry->atom, entry->result, result);
              inform (entry->location,
@@ -2949,10 +2950,13 @@ tree
  finish_requires_expr (location_t loc, tree parms, tree reqs)
  {
    /* Build the node.  */
-  tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs, 
NULL_TREE);
+  tree r = make_node (REQUIRES_EXPR);
+  TREE_TYPE (r) = boolean_type_node;
+  REQUIRES_EXPR_PARMS (r) = parms;
+  REQUIRES_EXPR_REQS (r) = reqs;
+  REQUIRES_EXPR_LOCATION (r) = loc;
    TREE_SIDE_EFFECTS (r) = false;
    TREE_CONSTANT (r) = true;
-  SET_EXPR_LOCATION (r, loc);
    return r;
  }
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index e8f1a7e8c34..0a67aa8ce14 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -227,6 +227,7 @@ cp_tree_size (enum tree_code code)
      case PRECONDITION_STMT:   return sizeof (tree_exp);
      case POSTCONDITION_STMT:  return sizeof (tree_exp);
      case TU_LOCAL_ENTITY:     return sizeof (tree_tu_local_entity);
+    case REQUIRES_EXPR:                return sizeof (tree_requires_expr);
      default:
        switch (TREE_CODE_CLASS (code))
        {
@@ -620,6 +621,7 @@ cp_common_init_ts (void)
    MARK_TS_TYPED (LAMBDA_EXPR);
    MARK_TS_TYPED (TYPE_ARGUMENT_PACK);
    MARK_TS_TYPED (TRAIT_EXPR);
+  MARK_TS_TYPED (REQUIRES_EXPR);
/* Random new trees. */
    MARK_TS_COMMON (BASELINK);
@@ -715,7 +717,6 @@ cp_common_init_ts (void)
    MARK_TS_EXP (DISJ_CONSTR);
    MARK_TS_EXP (ATOMIC_CONSTR);
    MARK_TS_EXP (NESTED_REQ);
-  MARK_TS_EXP (REQUIRES_EXPR);
    MARK_TS_EXP (SIMPLE_REQ);
    MARK_TS_EXP (TYPE_REQ);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 7b3cee5cf6e..7539e55f900 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -500,13 +500,13 @@ DEFTREECODE (OMP_DEPOBJ, "omp_depobj", tcc_statement, 2)
  /* Used to represent information associated with constrained declarations. */
  DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0)
-/* A requires-expr has three operands. The first operand is
-   its parameter list (possibly NULL). The second is a list of
-   requirements, which are denoted by the _REQ* tree codes
-   below.  The third is a TREE_VEC of template arguments to
+/* A requires-expr has three operands.  REQUIRES_EXPR_PARMS is
+   its parameter list (possibly NULL).  REQUIRES_EXPR_REQS is a list of
+   requirements, which are denoted by the _REQ* tree codes below.
+   REQUIRES_EXPR_EXTRA_ARGS is a TREE_VEC of template arguments to
     be applied when substituting into the parameter list and
     requirements, set by tsubst_requires_expr for partial instantiations.  */
-DEFTREECODE (REQUIRES_EXPR,   "requires_expr", tcc_expression, 3)
+DEFTREECODE (REQUIRES_EXPR, "requires_expr", tcc_exceptional, 0)
/* A requirement for an expression. */
  DEFTREECODE (SIMPLE_REQ, "simple_req", tcc_expression, 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8dac8c98b7a..6c34551f299 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1885,21 +1885,33 @@ struct GTY(()) tree_tu_local_entity {
  #define TU_LOCAL_ENTITY_LOCATION(NODE) \
    (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->loc)
-
+/* Representation of a requires-expression.  */
+struct GTY(()) tree_requires_expr {
+  struct tree_typed typed;
+  tree parms;
+  tree reqs;
+  tree extra_args;
+  location_t loc;
+};
+
  /* The list of local parameters introduced by this requires-expression,
     in the form of a chain of PARM_DECLs.  */
  #define REQUIRES_EXPR_PARMS(NODE) \
-  TREE_OPERAND (TREE_CHECK (NODE, REQUIRES_EXPR), 0)
+  (((struct tree_requires_expr *) REQUIRES_EXPR_CHECK (NODE))->parms)
/* A TREE_LIST of the requirements for this requires-expression.
     The requirements are stored in lexical order within the TREE_VALUE
     of each TREE_LIST node.  The TREE_PURPOSE of each node is unused.  */
  #define REQUIRES_EXPR_REQS(NODE) \
-  TREE_OPERAND (TREE_CHECK (NODE, REQUIRES_EXPR), 1)
+  (((struct tree_requires_expr *) REQUIRES_EXPR_CHECK (NODE))->reqs)
/* Like PACK_EXPANSION_EXTRA_ARGS, for requires-expressions. */
  #define REQUIRES_EXPR_EXTRA_ARGS(NODE) \
-  TREE_OPERAND (TREE_CHECK (NODE, REQUIRES_EXPR), 2)
+  (((struct tree_requires_expr *) REQUIRES_EXPR_CHECK (NODE))->extra_args)
+
+/* The source location of the requires-expression.  */
+#define REQUIRES_EXPR_LOCATION(NODE) \
+  (((struct tree_requires_expr *) REQUIRES_EXPR_CHECK (NODE))->loc)
/* True iff TYPE is cv decltype(^^int). */
  #define REFLECTION_TYPE_P(TYPE) (TREE_CODE (TYPE) == META_TYPE)
@@ -2029,7 +2041,8 @@ enum cp_tree_node_structure_enum {
    TS_CP_TEMPLATE_INFO,
    TS_CP_CONSTRAINT_INFO,
    TS_CP_USERDEF_LITERAL,
-  TS_CP_TU_LOCAL_ENTITY
+  TS_CP_TU_LOCAL_ENTITY,
+  TS_CP_REQUIRES_EXPR
  };
/* The resulting tree type. */
@@ -2062,6 +2075,8 @@ union GTY((desc ("cp_tree_node_structure (&%h)"),
      userdef_literal;
    struct tree_tu_local_entity GTY ((tag ("TS_CP_TU_LOCAL_ENTITY")))
      tu_local_entity;
+  struct tree_requires_expr GTY ((tag ("TS_CP_REQUIRES_EXPR")))
+    requires_expr;
  };
@@ -9122,6 +9137,8 @@ cp_expr_location (const_tree t_)
        return TRAIT_EXPR_LOCATION (t);
      case PTRMEM_CST:
        return PTRMEM_CST_LOCATION (t);
+    case REQUIRES_EXPR:
+      return REQUIRES_EXPR_LOCATION (t);
      default:
        return EXPR_LOCATION (t);
      }
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index db13a83ff90..77bff900189 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -2830,7 +2830,7 @@ pp_cxx_requires_expr (cxx_pretty_printer *pp, tree t)
        pp_cxx_right_paren (pp);
        pp_cxx_whitespace (pp);
      }
-  pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1));
+  pp_cxx_requirement_body (pp, REQUIRES_EXPR_REQS (t));
  }
/* simple-requirement:
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a2233107317..472f81ea9e1 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -21225,6 +21225,7 @@ cp_tree_node_structure (union lang_tree_node * t)
      case TRAIT_EXPR:          return TS_CP_TRAIT_EXPR;
      case TU_LOCAL_ENTITY:     return TS_CP_TU_LOCAL_ENTITY;
      case USERDEF_LITERAL:     return TS_CP_USERDEF_LITERAL;
+    case REQUIRES_EXPR:                return TS_CP_REQUIRES_EXPR;
      default:                  return TS_CP_GENERIC;
      }
  }
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index c2fb6027c52..a0abd74ac32 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -4527,7 +4527,7 @@ print_requires_expression_info (diagnostics::text_sink 
&text_output,
    cxx_pretty_printer *const pp
      = static_cast <cxx_pretty_printer *> (text_output.get_printer ());
- tree parms = TREE_OPERAND (expr, 0);
+  tree parms = REQUIRES_EXPR_PARMS (expr);
    pp_verbatim (pp, parms ? G_("in requirements with ")
                         : G_("in requirements "));
    while (parms)
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index d368359dccd..4ec57a54c34 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -888,14 +888,14 @@ write_tparms_constraints (tree constraints)
      {
        tree probe = constraints;
        while (probe
-            && !EXPR_LOCATION (probe)
+            && !cp_expr_location (probe)
             && TREE_CODE (probe) == TRUTH_ANDIF_EXPR)
        {
          tree op1 = TREE_OPERAND (probe, 1);
-         probe = (EXPR_LOCATION (op1) ? op1
+         probe = (cp_expr_location (op1) ? op1
                   : TREE_OPERAND (probe, 0));
        }
-      if (probe && EXPR_LOCATION (probe))
+      if (probe && cp_expr_location (probe))
        {
          write_char ('Q');
          write_constraint_expression (probe);
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index da0e0e0aae3..4057d916c22 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -6546,17 +6546,22 @@ trees_out::core_vals (tree t)
        unsigned limit = (vl ? VL_EXP_OPERAND_LENGTH (t)
                        : TREE_OPERAND_LENGTH (t));
        unsigned ix = unsigned (vl);
-      if (code == REQUIRES_EXPR)
-       {
-         /* The first operand of a REQUIRES_EXPR is a tree chain
-            of PARM_DECLs.  We need to stream this separately as
-            otherwise we would only stream the first one.  */
-         chained_decls (REQUIRES_EXPR_PARMS (t));
-         ++ix;
-       }
        for (; ix != limit; ix++)
        WT (TREE_OPERAND (t, ix));
      }
+  else if (code == REQUIRES_EXPR)
+    {
+      if (state)
+       state->write_location (*this, REQUIRES_EXPR_LOCATION (t));
+
+      if (streaming_p ())
+       if (has_warning_spec (t))
+         u (get_warning_spec (t));
+
+      chained_decls (REQUIRES_EXPR_PARMS (t));
+      WT (REQUIRES_EXPR_REQS (t));
+      WT (REQUIRES_EXPR_EXTRA_ARGS (t));
+    }
    else
      /* The CODE_CONTAINS tables were inaccurate when I started.  */
      gcc_checking_assert (TREE_CODE_CLASS (code) != tcc_expression
@@ -7141,14 +7146,19 @@ trees_in::core_vals (tree t)
        unsigned limit = (vl ? VL_EXP_OPERAND_LENGTH (t)
                        : TREE_OPERAND_LENGTH (t));
        unsigned ix = unsigned (vl);
-      if (code == REQUIRES_EXPR)
-       {
-         REQUIRES_EXPR_PARMS (t) = chained_decls ();
-         ++ix;
-       }
        for (; ix != limit; ix++)
        RTU (TREE_OPERAND (t, ix));
      }
+  else if (code == REQUIRES_EXPR)
+    {
+      REQUIRES_EXPR_LOCATION (t) = state->read_location (*this);
+      if (has_warning_spec (t))
+       put_warning_spec (t, u ());
+
+      REQUIRES_EXPR_PARMS (t) = chained_decls ();
+      RTU (REQUIRES_EXPR_REQS (t));
+      RTU (REQUIRES_EXPR_EXTRA_ARGS (t));
+    }
/* Then by CODE. Special cases and/or 1:1 tree shape
       correspondence. */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 3c110d7fa1c..d571d6918af 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -1886,6 +1886,11 @@ iterative_hash_template_arg (tree arg, hashval_t val)
           So just hash the closure type.  */
        return iterative_hash_template_arg (TREE_TYPE (arg), val);
+ case REQUIRES_EXPR:
+      val = iterative_hash_template_arg (REQUIRES_EXPR_PARMS (arg), val);
+      val = iterative_hash_template_arg (REQUIRES_EXPR_REQS (arg), val);
+      return iterative_hash_template_arg (REQUIRES_EXPR_EXTRA_ARGS (arg), val);
+
      case CAST_EXPR:
      case IMPLICIT_CONV_EXPR:
      case STATIC_CAST_EXPR:
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index e543cf14519..59629b09fa5 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -2050,6 +2050,7 @@ strip_typedefs_expr (tree t, bool *remove_attributes, 
unsigned int flags)
      case OVERLOAD:
      case BASELINK:
      case ARGUMENT_PACK_SELECT:
+    case REQUIRES_EXPR:
        return t;
case TRAIT_EXPR:
@@ -4527,6 +4528,16 @@ cp_tree_equal (tree t1, tree t2)
      case REFLECT_EXPR:
        return compare_reflections (t1, t2);
+ case REQUIRES_EXPR:
+      if (cp_tree_equal (REQUIRES_EXPR_PARMS (t1),
+                         REQUIRES_EXPR_PARMS (t2))
+         && cp_tree_equal (REQUIRES_EXPR_REQS (t1),
+                           REQUIRES_EXPR_REQS (t2))
+         && cp_tree_equal (REQUIRES_EXPR_EXTRA_ARGS (t1),
+                           REQUIRES_EXPR_EXTRA_ARGS (t2)))
+       return true;
+      return false;
+
      default:
        break;
      }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr126066.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr126066.C
new file mode 100644
index 00000000000..ff8a3a49261
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr126066.C
@@ -0,0 +1,15 @@
+// PR c++/126066
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wall" }
+
+struct S { S& operator=(int); };
+
+void
+fn ()
+{
+  if constexpr (requires(int &a) { a = 0; }) { }
+  if constexpr (requires(S &s) { s = 0; }) { }
+}
+
+constexpr bool b = requires(int &a) { a = 0; };
+static_assert(requires(int &a) { a = 0; });

base-commit: f935a7a9013d0748ba1f42dc532df3d278707bf6

Reply via email to