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