c++: Implement CWG3149 - Rvalues in destructuring expansion statements
Commit Message
Hi!
The following patch implements CWG3149. If expansion_init
is not an lvalue in destructiring expansion stmt, init is wrapped
in a static_cast to decltype(u_i)&&.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2026-04-08 Jakub Jelinek <jakub@redhat.com>
* pt.cc (finish_expansion_stmt): Implement CWG3149 - Rvalues in
destructuring expansion statements. For esk_destructuring, if
expansion_init is not lvalue_p, wrap init into a static_cast
to decltype of destruct_decls[i].
* g++.dg/cpp26/expansion-stmt39.C: New test.
* g++.dg/reflect/cwg3149.C: New test.
Jakub
@@ -33313,6 +33313,7 @@ finish_expansion_stmt (tree expansion_st
location_t loc = DECL_SOURCE_LOCATION (range_decl);
tree begin = NULL_TREE, begin_minus_begin_type = NULL_TREE;
auto_vec<tree, 8> destruct_decls;
+ bool is_lvalue = false;
if (BRACE_ENCLOSED_INITIALIZER_P (expansion_init))
{
/* Enumerating expansion statements. */
@@ -33375,6 +33376,7 @@ finish_expansion_stmt (tree expansion_st
if (sz < 0)
return;
n = sz;
+ is_lvalue = lvalue_p (expansion_init);
tree auto_node = make_auto ();
tree decomp_type = cp_build_reference_type (auto_node, true);
decomp_type = do_auto_deduction (decomp_type, expansion_init, auto_node);
@@ -33501,6 +33503,18 @@ finish_expansion_stmt (tree expansion_st
break;
case esk_destructuring:
init = convert_from_reference (destruct_decls[i]);
+ if (!is_lvalue)
+ {
+ tree ctype;
+ if (DECL_HAS_VALUE_EXPR_P (destruct_decls[i]))
+ ctype = unlowered_expr_type (destruct_decls[i]);
+ else
+ ctype = lookup_decomp_type (destruct_decls[i]);
+ ctype = cp_build_reference_type (ctype, /*rval=*/true);
+ init = build_static_cast (loc, ctype, init,
+ tf_warning_or_error);
+ init = convert_from_reference (init);
+ }
break;
default:
gcc_unreachable ();
@@ -0,0 +1,16 @@
+// CWG3149 - Rvalues in destructuring expansion statements
+// { dg-do compile { target c++26 } }
+
+#include <tuple>
+#include <type_traits>
+
+std::tuple <int, long long &, int &&> foo ();
+
+void
+bar ()
+{
+ template for (auto &&elem : foo ())
+ static_assert (std::is_same_v <std::remove_reference_t <decltype (elem)>, long long>
+ ? std::is_same_v <decltype (elem), long long &>
+ : std::is_same_v <decltype (elem), int &&>);
+}
@@ -0,0 +1,17 @@
+// CWG3149 - Rvalues in destructuring expansion statements
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+#include <tuple>
+
+std::tuple <int, long long &, int &&> foo ();
+
+void
+bar ()
+{
+ template for (auto &&elem : foo ())
+ static_assert (remove_reference (type_of (^^elem)) == ^^long long
+ ? type_of (^^elem) == ^^long long &
+ : type_of (^^elem) == ^^int &&);
+}