On 2/22/26 5:34 AM, Jakub Jelinek wrote:
Hi!
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.
Bootstrapped/regtested on x86_64-linux, ok for trunk?
Please note in the cp_perform_range_for_lookup comment that tf_none
changes the behavior more than it usually does. OK with that addition.
2026-02-21 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.
--- gcc/cp/parser.cc.jj 2026-02-19 08:19:26.714624277 +0100
+++ gcc/cp/parser.cc 2026-02-20 13:32:53.454217299 +0100
@@ -16243,7 +16243,8 @@ cp_perform_range_for_lookup (tree range,
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 +16252,20 @@ cp_perform_range_for_lookup (tree range,
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))
+ (complain & tf_error) ? complain
+ : tf_any_viable);
+ if ((complain & tf_error) == 0)
{
- /* 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)
+ if (*begin == error_mark_node || *end == 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)
- {
- *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;
}
}
--- gcc/cp/pt.cc.jj 2026-02-19 12:32:15.253353569 +0100
+++ gcc/cp/pt.cc 2026-02-20 13:29:35.824439071 +0100
@@ -33226,7 +33226,7 @@ finish_expansion_stmt (tree expansion_st
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)
--- gcc/testsuite/g++.dg/cpp26/expansion-stmt31.C.jj 2026-02-20
13:43:47.826561109 +0100
+++ gcc/testsuite/g++.dg/cpp26/expansion-stmt31.C 2026-02-20
13:43:35.645572855 +0100
@@ -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> ())
+ ;
+}
Jakub