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; });
