c++, v2: Implement CWG3135 - constexpr structured bindings with prvalues from tuples

Message ID adVR7TDRAZ1Oiope@tucnak
State New
Headers
Series c++, v2: Implement CWG3135 - constexpr structured bindings with prvalues from tuples |

Commit Message

Jakub Jelinek April 7, 2026, 6:50 p.m. UTC
  On Tue, Apr 07, 2026 at 05:51:53PM +0200, Jakub Jelinek wrote:
> On Tue, Apr 07, 2026 at 11:44:15AM -0400, Jason Merrill wrote:
> > > The pr108503.C testcase was the only one affected in the testsuite.
> > 
> > I've gotten a couple of CWG responses now agreeing that it should have been
> > a DR.  Do you have an opinion about whether to go ahead with dropping the
> > cxx_dialect check now, or put the patch in as is and drop it for GCC 17?  I
> > lean slightly toward making the change now, the effect on any existing C++20
> > code ought to be very small since any use of the binding name is an lvalue
> > either way.
> 
> I'll test a version which assumes it is a DR tonight.

Here is so far lightly tested patch, ok for trunk if it passes full
bootstrap/regtest?

2026-04-07  Jakub Jelinek  <jakub@redhat.com>

	* decl.cc (cp_finish_decomp): Implement CWG3135 - constexpr structured
	bindings with prvalues from tuples (as a DR).  Don't call
	cp_build_reference_type if init is prvalue.

	* g++.dg/cpp26/decomp30.C: New test.
	* g++.dg/gomp/pr108503.C: Adjust expected diagnostic locations.



	Jakub
  

Comments

Jason Merrill April 7, 2026, 8:31 p.m. UTC | #1
On 4/7/26 2:50 PM, Jakub Jelinek wrote:
> On Tue, Apr 07, 2026 at 05:51:53PM +0200, Jakub Jelinek wrote:
>> On Tue, Apr 07, 2026 at 11:44:15AM -0400, Jason Merrill wrote:
>>>> The pr108503.C testcase was the only one affected in the testsuite.
>>>
>>> I've gotten a couple of CWG responses now agreeing that it should have been
>>> a DR.  Do you have an opinion about whether to go ahead with dropping the
>>> cxx_dialect check now, or put the patch in as is and drop it for GCC 17?  I
>>> lean slightly toward making the change now, the effect on any existing C++20
>>> code ought to be very small since any use of the binding name is an lvalue
>>> either way.
>>
>> I'll test a version which assumes it is a DR tonight.
> 
> Here is so far lightly tested patch, ok for trunk if it passes full
> bootstrap/regtest?

OK.

> 2026-04-07  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* decl.cc (cp_finish_decomp): Implement CWG3135 - constexpr structured
> 	bindings with prvalues from tuples (as a DR).  Don't call
> 	cp_build_reference_type if init is prvalue.
> 
> 	* g++.dg/cpp26/decomp30.C: New test.
> 	* g++.dg/gomp/pr108503.C: Adjust expected diagnostic locations.
> 
> --- gcc/cp/decl.cc.jj	2026-04-06 23:08:40.440491516 +0200
> +++ gcc/cp/decl.cc	2026-04-07 20:42:04.966830492 +0200
> @@ -11025,7 +11025,8 @@ cp_finish_decomp (tree decl, cp_decomp *
>   		  maybe_push_decl (t);
>   		  /* Save the decltype away before reference collapse.  */
>   		  hash_map_safe_put<hm_ggc> (decomp_type_table, t, eltype);
> -		  eltype = cp_build_reference_type (eltype, !lvalue_p (init));
> +		  if (glvalue_p (init))
> +		    eltype = cp_build_reference_type (eltype, !lvalue_p (init));
>   		  TREE_TYPE (t) = eltype;
>   		  layout_decl (t, 0);
>   		  DECL_HAS_VALUE_EXPR_P (t) = 0;
> @@ -11070,7 +11071,8 @@ cp_finish_decomp (tree decl, cp_decomp *
>   	    }
>   	  /* Save the decltype away before reference collapse.  */
>   	  hash_map_safe_put<hm_ggc> (decomp_type_table, v[i], eltype);
> -	  eltype = cp_build_reference_type (eltype, !lvalue_p (init));
> +	  if (glvalue_p (init))
> +	    eltype = cp_build_reference_type (eltype, !lvalue_p (init));
>   	  TREE_TYPE (v[i]) = eltype;
>   	  layout_decl (v[i], 0);
>   	  if (DECL_HAS_VALUE_EXPR_P (v[i]))
> --- gcc/testsuite/g++.dg/cpp26/decomp30.C.jj	2026-04-07 20:41:25.413496275 +0200
> +++ gcc/testsuite/g++.dg/cpp26/decomp30.C	2026-04-07 20:45:04.141841379 +0200
> @@ -0,0 +1,35 @@
> +// CWG3135 - constexpr structured bindings with prvalues from tuples
> +// { dg-do compile { target c++14 } }
> +// { dg-options "" }
> +
> +namespace std {
> +  template <typename T> struct tuple_size;
> +  template <int, typename> struct tuple_element;
> +}
> +
> +struct A {
> +  template <int I> constexpr int get () const { return I + 6; }
> +};
> +
> +template <> struct std::tuple_size <A> { static const int value = 2; };
> +template <int I> struct std::tuple_element <I, A> { using type = int; };
> +template <> struct std::tuple_size <const A> { static const int value = 2; };
> +template <int I> struct std::tuple_element <I, const A> { using type = const int; };
> +
> +constexpr A
> +bar ()
> +{
> +  return A {};
> +}
> +
> +template <int N>
> +constexpr int
> +foo ()
> +{
> +  constexpr auto [...i] = bar ();	// { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } }
> +					// { dg-warning "structured binding declaration can be 'constexpr' only with" "" { target c++23_down } .-1 }
> +					// { dg-warning "structured bindings only available with" "" { target c++14_down } .-2 }
> +  return i...[0] + i...[1];		// { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +}
> +
> +static_assert (foo <42> () == 13);
> --- gcc/testsuite/g++.dg/gomp/pr108503.C.jj	2026-03-27 10:17:15.840302900 +0100
> +++ gcc/testsuite/g++.dg/gomp/pr108503.C	2026-04-07 20:46:43.518183216 +0200
> @@ -21,7 +21,7 @@ void
>   foo (B a)
>   {
>     #pragma omp for collapse(2)
> -  for (auto [i, j, k] : a)
> -    for (int l = i; l < j; l += k)	// { dg-error "initializer expression refers to iteration variable 'i'" }
> -      ;					// { dg-error "condition expression refers to iteration variable 'j'" "" { target *-*-* } .-1 }
> -}					// { dg-error "increment expression refers to iteration variable 'k'" "" { target *-*-* } .-2 }
> +  for (auto [i, j, k] : a)		// { dg-error "initializer expression refers to iteration variable 'i'" }
> +    for (int l = i; l < j; l += k)	// { dg-error "condition expression refers to iteration variable 'j'" }
> +      ;					// { dg-error "increment expression refers to iteration variable 'k'" "" { target *-*-* } .-2 }
> +}
> 
> 
> 	Jakub
>
  

Patch

--- gcc/cp/decl.cc.jj	2026-04-06 23:08:40.440491516 +0200
+++ gcc/cp/decl.cc	2026-04-07 20:42:04.966830492 +0200
@@ -11025,7 +11025,8 @@  cp_finish_decomp (tree decl, cp_decomp *
 		  maybe_push_decl (t);
 		  /* Save the decltype away before reference collapse.  */
 		  hash_map_safe_put<hm_ggc> (decomp_type_table, t, eltype);
-		  eltype = cp_build_reference_type (eltype, !lvalue_p (init));
+		  if (glvalue_p (init))
+		    eltype = cp_build_reference_type (eltype, !lvalue_p (init));
 		  TREE_TYPE (t) = eltype;
 		  layout_decl (t, 0);
 		  DECL_HAS_VALUE_EXPR_P (t) = 0;
@@ -11070,7 +11071,8 @@  cp_finish_decomp (tree decl, cp_decomp *
 	    }
 	  /* Save the decltype away before reference collapse.  */
 	  hash_map_safe_put<hm_ggc> (decomp_type_table, v[i], eltype);
-	  eltype = cp_build_reference_type (eltype, !lvalue_p (init));
+	  if (glvalue_p (init))
+	    eltype = cp_build_reference_type (eltype, !lvalue_p (init));
 	  TREE_TYPE (v[i]) = eltype;
 	  layout_decl (v[i], 0);
 	  if (DECL_HAS_VALUE_EXPR_P (v[i]))
--- gcc/testsuite/g++.dg/cpp26/decomp30.C.jj	2026-04-07 20:41:25.413496275 +0200
+++ gcc/testsuite/g++.dg/cpp26/decomp30.C	2026-04-07 20:45:04.141841379 +0200
@@ -0,0 +1,35 @@ 
+// CWG3135 - constexpr structured bindings with prvalues from tuples
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A {
+  template <int I> constexpr int get () const { return I + 6; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 2; };
+template <int I> struct std::tuple_element <I, A> { using type = int; };
+template <> struct std::tuple_size <const A> { static const int value = 2; };
+template <int I> struct std::tuple_element <I, const A> { using type = const int; };
+
+constexpr A
+bar ()
+{
+  return A {};
+}
+
+template <int N>
+constexpr int
+foo ()
+{
+  constexpr auto [...i] = bar ();	// { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } }
+					// { dg-warning "structured binding declaration can be 'constexpr' only with" "" { target c++23_down } .-1 }
+					// { dg-warning "structured bindings only available with" "" { target c++14_down } .-2 }
+  return i...[0] + i...[1];		// { dg-warning "pack indexing only available with" "" { target c++23_down } }
+}
+
+static_assert (foo <42> () == 13);
--- gcc/testsuite/g++.dg/gomp/pr108503.C.jj	2026-03-27 10:17:15.840302900 +0100
+++ gcc/testsuite/g++.dg/gomp/pr108503.C	2026-04-07 20:46:43.518183216 +0200
@@ -21,7 +21,7 @@  void
 foo (B a)
 {
   #pragma omp for collapse(2)
-  for (auto [i, j, k] : a)
-    for (int l = i; l < j; l += k)	// { dg-error "initializer expression refers to iteration variable 'i'" }
-      ;					// { dg-error "condition expression refers to iteration variable 'j'" "" { target *-*-* } .-1 }
-}					// { dg-error "increment expression refers to iteration variable 'k'" "" { target *-*-* } .-2 }
+  for (auto [i, j, k] : a)		// { dg-error "initializer expression refers to iteration variable 'i'" }
+    for (int l = i; l < j; l += k)	// { dg-error "condition expression refers to iteration variable 'j'" }
+      ;					// { dg-error "increment expression refers to iteration variable 'k'" "" { target *-*-* } .-2 }
+}