https://gcc.gnu.org/g:ec2b5122e1f52409f7207539cc821424fa209ad7

commit r17-2361-gec2b5122e1f52409f7207539cc821424fa209ad7
Author: Marek Polacek <[email protected]>
Date:   Tue Jul 7 16:52:39 2026 -0400

    c++: ICE with requires and -Wsequence-point [PR126066]
    
    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.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 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 ++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-pr126066.C | 15 +++++++++++
 13 files changed, 99 insertions(+), 34 deletions(-)

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index c298ff11a83c..a0debcc49762 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 c4118788d4ce..08439293c649 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 e8f1a7e8c346..0a67aa8ce14b 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 7b3cee5cf6e7..7539e55f9009 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 8dac8c98b7a3..6c34551f2997 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 db13a83ff902..77bff900189e 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 a22331073173..472f81ea9e1f 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 c2fb6027c521..a0abd74ac32f 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 d368359dccd6..4ec57a54c340 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 da0e0e0aae3a..4057d916c227 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 3c110d7fa1c0..d571d6918afb 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 e543cf145195..59629b09fa52 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 000000000000..ff8a3a492611
--- /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; });

Reply via email to