c++: Implement CWG3149 - Rvalues in destructuring expansion statements

Message ID adXvSi9HJkvYW5fB@tucnak
State New
Headers
Series c++: Implement CWG3149 - Rvalues in destructuring expansion statements |

Commit Message

Jakub Jelinek April 8, 2026, 6:01 a.m. UTC
  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
  

Patch

--- gcc/cp/pt.cc.jj	2026-04-06 23:10:37.311524244 +0200
+++ gcc/cp/pt.cc	2026-04-07 09:27:33.149372737 +0200
@@ -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 ();
--- gcc/testsuite/g++.dg/cpp26/expansion-stmt39.C.jj	2026-04-07 09:49:42.588856656 +0200
+++ gcc/testsuite/g++.dg/cpp26/expansion-stmt39.C	2026-04-07 09:54:56.389542680 +0200
@@ -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 &&>);
+}
--- gcc/testsuite/g++.dg/reflect/cwg3149.C.jj	2026-04-07 09:40:10.554543795 +0200
+++ gcc/testsuite/g++.dg/reflect/cwg3149.C	2026-04-07 09:39:07.591610057 +0200
@@ -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 &&);
+}