https://gcc.gnu.org/g:48b8d2dd48aaec275e2bdd84042a0adde9c953d8

commit r16-7629-g48b8d2dd48aaec275e2bdd84042a0adde9c953d8
Author: Jakub Jelinek <[email protected]>
Date:   Sun Feb 22 22:07:45 2026 +0100

    c++: Fix up CWG 3123 expansion stmt handling once again [PR124184]
    
    Barry Revzin mentioned in private mail that we reject the following
    testcase even when it should be accepted.
    The problem is that I thought finish_call_expr with tf_none would
    be just harmless, but it is not, it can result in errors while trying
    to instantiate something else (e.g. the noexcept).
    So, instead of doing finish_call_expr with tf_none and only if that
    returns error_mark_node calling it again with tf_any_viable, the
    following patch just calls it only with tf_any_viable immediately.
    
    2026-02-22  Jakub Jelinek  <[email protected]>
    
            PR c++/124184
            * parser.cc (cp_perform_range_for_lookup): If tf_error is
            missing, call finish_call_expr after perform_koenig_lookup
            calls immediately with tf_any_viable instead of twice, once
            with tf_none and then with tf_any_viable.
            * pt.cc (finish_expansion_stmt): Remove unneeded parens.
    
            * g++.dg/cpp26/expansion-stmt31.C: New test.

Diff:
---
 gcc/cp/parser.cc                              | 42 +++++++++++++--------------
 gcc/cp/pt.cc                                  |  2 +-
 gcc/testsuite/g++.dg/cpp26/expansion-stmt31.C | 32 ++++++++++++++++++++
 3 files changed, 53 insertions(+), 23 deletions(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 13b9b8f46b47..7421cc68ff49 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16178,7 +16178,13 @@ cp_convert_range_for (tree statement, tree range_decl, 
tree range_expr,
 /* Solves BEGIN_EXPR and END_EXPR as described in cp_convert_range_for.
    We need to solve both at the same time because the method used
    depends on the existence of members begin or end.
-   Returns the type deduced for the iterator expression.  */
+   Returns the type deduced for the iterator expression.
+   This function assumes that if COMPLAIN is not tf_warning_or_error,
+   it is tf_none and is called to find out if an expansion statement
+   is iterating (vs. destructruring) and behaves differently in that
+   case, in particular just checks if ADL looked up begin/end has
+   any viable candidates rather than doing full finish_call_expr
+   in that case.  */
 
 tree
 cp_perform_range_for_lookup (tree range, tree *begin, tree *end,
@@ -16243,7 +16249,8 @@ cp_perform_range_for_lookup (tree range, tree *begin, 
tree *end,
          if ((complain & tf_error) == 0 && member_begin == id_begin)
            return error_mark_node;
          *begin = finish_call_expr (member_begin, &vec, false, true,
-                                    complain);
+                                    (complain & tf_error) ? complain
+                                    : tf_any_viable);
          member_end = perform_koenig_lookup (id_end, vec, complain);
          if ((complain & tf_error) == 0 && member_end == id_end)
            {
@@ -16251,29 +16258,20 @@ cp_perform_range_for_lookup (tree range, tree *begin, 
tree *end,
              return error_mark_node;
            }
          *end = finish_call_expr (member_end, &vec, false, true,
-                                  complain);
-         if ((complain & tf_error) == 0
-             && (*begin == error_mark_node || *end == error_mark_node))
-           {
-             /* Expansion stmt should be iterating if there are any
-                viable candidates for begin and end.  If both finish_call_expr
-                with tf_none succeeded, there certainly are, if not,
-                retry with tf_any_viable to check if there were any viable
-                candidates.  */
-             if (*begin == error_mark_node
-                 && finish_call_expr (member_begin, &vec, false, true,
-                                      tf_any_viable) == error_mark_node)
-               {
-                 *end = error_mark_node;
-                 return error_mark_node;
-               }
-             if (*end == error_mark_node
-                 && finish_call_expr (member_end, &vec, false, true,
-                                      tf_any_viable) == error_mark_node)
+                                  (complain & tf_error) ? complain
+                                  : tf_any_viable);
+         if ((complain & tf_error) == 0)
+           {
+             if (*begin == error_mark_node || *end == error_mark_node)
                {
-                 *begin = error_mark_node;
+                 *begin = *end = error_mark_node;
+                 /* Expansion stmt should be destructuring if no viable
+                    candidate was found.  */
                  return error_mark_node;
                }
+             /* Otherwise both are viable, so make sure to return
+                NULL_TREE and set *begin and *end to error_mark_node.  */
+             *begin = error_mark_node;
            }
        }
 
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 5d988ac52004..11711ee3543e 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -33226,7 +33226,7 @@ finish_expansion_stmt (tree expansion_stmt, tree args,
       iter_type = cp_perform_range_for_lookup (range_temp, &begin_expr,
                                               &end_expr, tf_none);
       if (iter_type != error_mark_node
-         || (begin_expr != error_mark_node && (end_expr != error_mark_node)))
+         || (begin_expr != error_mark_node && end_expr != error_mark_node))
        kind = esk_iterating;
     }
   if (kind == esk_iterating)
diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt31.C 
b/gcc/testsuite/g++.dg/cpp26/expansion-stmt31.C
new file mode 100644
index 000000000000..6fa0fa4d23df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt31.C
@@ -0,0 +1,32 @@
+// CWG 3123
+// { dg-do compile { target c++26 } }
+
+namespace N {
+template <class C>
+auto begin (C &c) noexcept (noexcept (c.begin ()))
+-> decltype (c.begin ());
+
+template <class C>
+auto end (C &c) noexcept (noexcept (c.end ()))
+-> decltype (c.end ());
+
+struct D { };
+}
+
+template <class T>
+struct E {
+int x;
+
+  struct F {
+    static_assert (!__is_same (T, N::D));
+  };
+
+  F begin ();
+};
+
+int
+main ()
+{
+  template for (auto elem : E <N::D> ())
+    ;
+}

Reply via email to