c++, v2: Fix up constexpr structured bindings used as condition [PR127109]
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap |
success
|
Build passed
|
Commit Message
On Tue, Sep 01, 2026 at 06:52:52PM -0400, Jason Merrill wrote:
> But now you have a TARGET_EXPR outside of any CLEANUP_POINT_EXPR. Since
> you're effectively declaring a non-temporary variable, better to do that
> directly rather than with a TARGET_EXPR, like the get_temp_regvar change I
> made for the cleanup guards.
Ok, that works too, though I had to tweak it a little bit.
As described on the DECL_DECOMP_BASE macro, the current behavior was
NULL_TREE -> this is a base var
VAR_DECL -> this is a structured binding and the VAR_DECL is the base var
corresponding to it
INTEGER_CST -> this is a base var of sb used as condition before
cp_finish_decomp (0 for if/while/for, 1 for switch)
TARGET_EXPR -> base of sb used as condition after cp_finish_decomp
Now, when we don't have a TARGET_EXPR, storing there just the plain
get_temp_regvar result wouldn't work, it wouldn't be distinguishable
from non-base var. So, I went with NON_LVALUE_EXPR around it.
Lightly tested so far, ok for trunk if it passes full bootstrap/regtest?
2026-09-02 Jakub Jelinek <jakub@redhat.com>
PR c++/127109
* decl.cc (cp_finish_decomp): Add [dcl.struct.bind]/7 reference
to comment. Use get_temp_regvar and pushdecl instead of
get_internal_target_expr and add_stmt. Set DECL_DECOMP_BASE
to NON_LVALUE_EXPR around the temp regvar.
* cp-tree.h (DECL_DECOMP_BASE): Adjust macro comment.
* semantics.cc (maybe_convert_cond, switch_finish_cond): Check for
NON_LVALUE_EXPR rather than TARGET_EXPR and extract its operand
rather than TARGET_EXPR_SLOT.
* g++.dg/cpp26/decomp32.C: New test.
Jakub
Comments
On 9/2/26 8:02 AM, Jakub Jelinek wrote:
> On Tue, Sep 01, 2026 at 06:52:52PM -0400, Jason Merrill wrote:
>> But now you have a TARGET_EXPR outside of any CLEANUP_POINT_EXPR. Since
>> you're effectively declaring a non-temporary variable, better to do that
>> directly rather than with a TARGET_EXPR, like the get_temp_regvar change I
>> made for the cleanup guards.
>
> Ok, that works too, though I had to tweak it a little bit.
> As described on the DECL_DECOMP_BASE macro, the current behavior was
> NULL_TREE -> this is a base var
> VAR_DECL -> this is a structured binding and the VAR_DECL is the base var
> corresponding to it
> INTEGER_CST -> this is a base var of sb used as condition before
> cp_finish_decomp (0 for if/while/for, 1 for switch)
> TARGET_EXPR -> base of sb used as condition after cp_finish_decomp
>
> Now, when we don't have a TARGET_EXPR, storing there just the plain
> get_temp_regvar result wouldn't work, it wouldn't be distinguishable
> from non-base var. So, I went with NON_LVALUE_EXPR around it.
>
> Lightly tested so far, ok for trunk if it passes full bootstrap/regtest?
OK.
> 2026-09-02 Jakub Jelinek <jakub@redhat.com>
>
> PR c++/127109
> * decl.cc (cp_finish_decomp): Add [dcl.struct.bind]/7 reference
> to comment. Use get_temp_regvar and pushdecl instead of
> get_internal_target_expr and add_stmt. Set DECL_DECOMP_BASE
> to NON_LVALUE_EXPR around the temp regvar.
> * cp-tree.h (DECL_DECOMP_BASE): Adjust macro comment.
> * semantics.cc (maybe_convert_cond, switch_finish_cond): Check for
> NON_LVALUE_EXPR rather than TARGET_EXPR and extract its operand
> rather than TARGET_EXPR_SLOT.
>
> * g++.dg/cpp26/decomp32.C: New test.
>
> --- a/gcc/cp/decl.cc 2026-09-02 10:05:24.578779537 +0200
> +++ b/gcc/cp/decl.cc 2026-09-02 13:41:20.670427577 +0200
> @@ -11105,7 +11105,10 @@ cp_finish_decomp (tree decl, cp_decomp *
> /* For structured bindings used in conditions we need to evaluate
> the conversion of decl (aka e in the standard) to bool or
> integral/enumeral type (the latter for switch conditions)
> - before the get methods. */
> + before the get methods, as [dcl.struct.bind]/7 requires that:
> + "The initialization of e and any conversion of e considered as
> + a decision variable is sequenced before the initialization of
> + any r_i." */
> tree cond = convert_from_reference (decl);
> if (integer_onep (DECL_DECOMP_BASE (decl)))
> /* switch condition. */
> @@ -11116,12 +11119,14 @@ cp_finish_decomp (tree decl, cp_decomp *
> cond = contextual_conv_bool (cond, tf_warning_or_error);
> if (cond && !error_operand_p (cond))
> {
> - /* Wrap that value into a TARGET_EXPR, emit it right
> - away and save for later uses in the cp_parse_condition
> - or its instantiation. */
> - cond = get_internal_target_expr (cond);
> - add_stmt (cond);
> - DECL_DECOMP_BASE (decl) = cond;
> + cond = get_temp_regvar (TREE_TYPE (cond), cond);
> + pushdecl (cond);
> + /* Set DECL_DECOMP_BASE to cond VAR_DECL wrapped in
> + NON_LVALUE_EXPR, such that it is considered to be
> + the condition of a structured binding rather than
> + structured binding's base variable. */
> + DECL_DECOMP_BASE (decl)
> + = build1 (NON_LVALUE_EXPR, TREE_TYPE (cond), cond);
> }
> }
> int save_read = DECL_READ_P (decl);
> --- a/gcc/cp/cp-tree.h 2026-09-02 10:05:24.575950822 +0200
> +++ b/gcc/cp/cp-tree.h 2026-09-02 12:58:54.936847764 +0200
> @@ -4845,7 +4845,7 @@ get_vec_init_expr (tree t)
> /* The underlying artificial VAR_DECL for structured binding. On the
> artificial base VAR_DECL this can be NULL, or integer_{zero,one}_node
> for structured binding used in if/while/for resp. switch conditions,
> - or a TARGET_EXPR with the condition value after cp_finish_decomp in
> + or a NON_LVALUE_EXPR with the condition value after cp_finish_decomp in
> those cases. */
> #define DECL_DECOMP_BASE(NODE) \
> (LANG_DECL_DECOMP_CHECK (NODE)->base)
> --- a/gcc/cp/semantics.cc 2026-09-02 10:05:24.579779524 +0200
> +++ b/gcc/cp/semantics.cc 2026-09-02 13:50:41.510182195 +0200
> @@ -1139,12 +1139,12 @@ maybe_convert_cond (tree cond)
> /* For structured binding used in condition, the conversion needs to be
> evaluated before the individual variables are initialized in the
> std::tuple_{size,element} case. cp_finish_decomp saved the conversion
> - result in a TARGET_EXPR, pick it up from there. */
> + result in a NON_LVALUE_EXPR, pick it up from there. */
> if (DECL_DECOMPOSITION_P (cond)
> && DECL_DECOMP_IS_BASE (cond)
> && DECL_DECOMP_BASE (cond)
> - && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
> - cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
> + && TREE_CODE (DECL_DECOMP_BASE (cond)) == NON_LVALUE_EXPR)
> + cond = TREE_OPERAND (DECL_DECOMP_BASE (cond), 0);
>
> if (warn_sequence_point && !processing_template_decl)
> verify_sequence_points (cond);
> @@ -1932,12 +1932,12 @@ finish_switch_cond (tree cond, tree swit
> /* For structured binding used in condition, the conversion needs to be
> evaluated before the individual variables are initialized in the
> std::tuple_{size,element} case. cp_finish_decomp saved the
> - conversion result in a TARGET_EXPR, pick it up from there. */
> + conversion result in a NON_LVALUE_EXPR, pick it up from there. */
> if (DECL_DECOMPOSITION_P (cond)
> && DECL_DECOMP_IS_BASE (cond)
> && DECL_DECOMP_BASE (cond)
> - && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
> - cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
> + && TREE_CODE (DECL_DECOMP_BASE (cond)) == NON_LVALUE_EXPR)
> + cond = TREE_OPERAND (DECL_DECOMP_BASE (cond), 0);
> cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, true);
> if (cond == NULL_TREE)
> {
> --- a/gcc/testsuite/g++.dg/cpp26/decomp32.C 2026-09-02 12:51:11.198952436 +0200
> +++ b/gcc/testsuite/g++.dg/cpp26/decomp32.C 2026-09-02 12:51:11.198952436 +0200
> @@ -0,0 +1,33 @@
> +// PR c++/127109
> +// { dg-do compile { target c++14 } }
> +// { dg-options "" }
> +
> +namespace std {
> + using size_t = decltype (sizeof 0);
> + template <typename> struct tuple_size;
> + template <size_t, typename> struct tuple_element;
> +}
> +
> +struct A {
> + int a, b;
> + bool c;
> + constexpr explicit operator bool () const { return c; }
> + template <std::size_t I>
> + constexpr const int &get () const { return I == 0 ? a : b; }
> +};
> +
> +template <>
> +struct std::tuple_size <A> { static constexpr int value = 2; };
> +template <std::size_t I>
> +struct std::tuple_element <I, A> { using type = const int; };
> +
> +constexpr int
> +foo (int v)
> +{
> + if (auto [a, b] = A { v, v * 2, v != 0 }) // { dg-warning "structured bindings in conditions only available with" "" { target c++23_down } }
> + return a + b;
> + return -1;
> +}
> +
> +static_assert (foo (1) == 3);
> +static_assert (foo (0) == -1);
>
>
> Jakub
>
@@ -11105,7 +11105,10 @@ cp_finish_decomp (tree decl, cp_decomp *
/* For structured bindings used in conditions we need to evaluate
the conversion of decl (aka e in the standard) to bool or
integral/enumeral type (the latter for switch conditions)
- before the get methods. */
+ before the get methods, as [dcl.struct.bind]/7 requires that:
+ "The initialization of e and any conversion of e considered as
+ a decision variable is sequenced before the initialization of
+ any r_i." */
tree cond = convert_from_reference (decl);
if (integer_onep (DECL_DECOMP_BASE (decl)))
/* switch condition. */
@@ -11116,12 +11119,14 @@ cp_finish_decomp (tree decl, cp_decomp *
cond = contextual_conv_bool (cond, tf_warning_or_error);
if (cond && !error_operand_p (cond))
{
- /* Wrap that value into a TARGET_EXPR, emit it right
- away and save for later uses in the cp_parse_condition
- or its instantiation. */
- cond = get_internal_target_expr (cond);
- add_stmt (cond);
- DECL_DECOMP_BASE (decl) = cond;
+ cond = get_temp_regvar (TREE_TYPE (cond), cond);
+ pushdecl (cond);
+ /* Set DECL_DECOMP_BASE to cond VAR_DECL wrapped in
+ NON_LVALUE_EXPR, such that it is considered to be
+ the condition of a structured binding rather than
+ structured binding's base variable. */
+ DECL_DECOMP_BASE (decl)
+ = build1 (NON_LVALUE_EXPR, TREE_TYPE (cond), cond);
}
}
int save_read = DECL_READ_P (decl);
@@ -4845,7 +4845,7 @@ get_vec_init_expr (tree t)
/* The underlying artificial VAR_DECL for structured binding. On the
artificial base VAR_DECL this can be NULL, or integer_{zero,one}_node
for structured binding used in if/while/for resp. switch conditions,
- or a TARGET_EXPR with the condition value after cp_finish_decomp in
+ or a NON_LVALUE_EXPR with the condition value after cp_finish_decomp in
those cases. */
#define DECL_DECOMP_BASE(NODE) \
(LANG_DECL_DECOMP_CHECK (NODE)->base)
@@ -1139,12 +1139,12 @@ maybe_convert_cond (tree cond)
/* For structured binding used in condition, the conversion needs to be
evaluated before the individual variables are initialized in the
std::tuple_{size,element} case. cp_finish_decomp saved the conversion
- result in a TARGET_EXPR, pick it up from there. */
+ result in a NON_LVALUE_EXPR, pick it up from there. */
if (DECL_DECOMPOSITION_P (cond)
&& DECL_DECOMP_IS_BASE (cond)
&& DECL_DECOMP_BASE (cond)
- && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
- cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
+ && TREE_CODE (DECL_DECOMP_BASE (cond)) == NON_LVALUE_EXPR)
+ cond = TREE_OPERAND (DECL_DECOMP_BASE (cond), 0);
if (warn_sequence_point && !processing_template_decl)
verify_sequence_points (cond);
@@ -1932,12 +1932,12 @@ finish_switch_cond (tree cond, tree swit
/* For structured binding used in condition, the conversion needs to be
evaluated before the individual variables are initialized in the
std::tuple_{size,element} case. cp_finish_decomp saved the
- conversion result in a TARGET_EXPR, pick it up from there. */
+ conversion result in a NON_LVALUE_EXPR, pick it up from there. */
if (DECL_DECOMPOSITION_P (cond)
&& DECL_DECOMP_IS_BASE (cond)
&& DECL_DECOMP_BASE (cond)
- && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
- cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
+ && TREE_CODE (DECL_DECOMP_BASE (cond)) == NON_LVALUE_EXPR)
+ cond = TREE_OPERAND (DECL_DECOMP_BASE (cond), 0);
cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, true);
if (cond == NULL_TREE)
{
@@ -0,0 +1,33 @@
+// PR c++/127109
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+namespace std {
+ using size_t = decltype (sizeof 0);
+ template <typename> struct tuple_size;
+ template <size_t, typename> struct tuple_element;
+}
+
+struct A {
+ int a, b;
+ bool c;
+ constexpr explicit operator bool () const { return c; }
+ template <std::size_t I>
+ constexpr const int &get () const { return I == 0 ? a : b; }
+};
+
+template <>
+struct std::tuple_size <A> { static constexpr int value = 2; };
+template <std::size_t I>
+struct std::tuple_element <I, A> { using type = const int; };
+
+constexpr int
+foo (int v)
+{
+ if (auto [a, b] = A { v, v * 2, v != 0 }) // { dg-warning "structured bindings in conditions only available with" "" { target c++23_down } }
+ return a + b;
+ return -1;
+}
+
+static_assert (foo (1) == 3);
+static_assert (foo (0) == -1);