[gcc r17-2361] c++: ICE with requires and -Wsequence-point [PR126066]

Marek Polacek via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
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; });
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.