https://gcc.gnu.org/g:85ba6b0eb100999a3e34a7731fc5977a139f117e

commit r16-9051-g85ba6b0eb100999a3e34a7731fc5977a139f117e
Author: Patrick Palka <[email protected]>
Date:   Wed Jun 3 18:03:14 2026 -0400

    c++: unchecked 'A op B' constraint-expression [PR125490]
    
    Here the nonsensical constraint-expression f() == 42 isn't getting rejected
    ahead of time because when parsing we pass no_toplevel_fold_p=true to
    cp_parser_binary_expression, which makes it use build_min instead of
    build_x_binary_op, effectively bypassing the actual processing of the
    operator expression until satisfaction time.  This flag was introduced
    by r145014 to avoid undesirable folding / canonicalization of OpenMP for
    loop conditions, and that's still the only other user of the flag today.
    
    But I think cp_parser_constraint_expression doesn't need to use this
    flag at all because constraint-expression parsing is always templated
    and we (generally) don't fold templated trees.  I think the flag is
    intended to be used only by OpenMP parsing routines.
    
    Note that without this patch we'd go on to reject the constraint-expression
    at satisfaction time, so this first testcase is effectively IFNDR / QoI.
    But this change isn't limited to IFNDR cases, we need to process the
    operator expression at parse time for sake of correct unqualified lookup
    as in the second testcase.
    
            PR c++/125490
    
    gcc/cp/ChangeLog:
    
            * parser.cc (cp_parser_constraint_expression): Pass
            no_toplevel_fold_p=false instead of =true to
            cp_parser_binary_expression.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp2a/concepts-pr125490.C: New test.
            * g++.dg/cpp2a/concepts-pr125490a.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>
    (cherry picked from commit f7990da34872cfb448d4d1341714508904cb666c)

Diff:
---
 gcc/cp/parser.cc                                |  2 +-
 gcc/testsuite/g++.dg/cpp2a/concepts-pr125490.C  | 10 ++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-pr125490a.C | 15 +++++++++++++++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index cc3ee315436a..f44450519ae3 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -34725,7 +34725,7 @@ cp_parser_constraint_expression (cp_parser *parser)
 {
   processing_constraint_expression_sentinel parsing_constraint;
   ++processing_template_decl;
-  cp_expr expr = cp_parser_binary_expression (parser, false, true,
+  cp_expr expr = cp_parser_binary_expression (parser, false, false,
                                              PREC_NOT_OPERATOR, NULL);
   --processing_template_decl;
   if (check_for_bare_parameter_packs (expr))
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr125490.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr125490.C
new file mode 100644
index 000000000000..a33416b232d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr125490.C
@@ -0,0 +1,10 @@
+// PR c++/125490
+// { dg-do compile { target c++20 } }
+
+void f();
+
+template<class T>
+concept C = f() == 42; // { dg-error "invalid operands" }
+
+template<class T>
+concept D = requires { requires f() == 42; }; // { dg-error "invalid operands" 
}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr125490a.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr125490a.C
new file mode 100644
index 000000000000..b1208fea7c96
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr125490a.C
@@ -0,0 +1,15 @@
+// PR c++/125490
+// { dg-do compile { target c++20 } }
+
+namespace N {
+  struct A { };
+}
+
+constexpr bool operator==(N::A, auto) { return true; } // #1
+
+template<class T>
+concept C = T{} == T{};
+
+constexpr bool operator==(N::A, N::A) { return false; } // #2
+
+static_assert(C<N::A>); // must only consider #1

Reply via email to