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?

For 16, maybe we can just remove the assert.

-- >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
-- 
2.55.0

Reply via email to