On 3/13/26 4:40 AM, Jakub Jelinek wrote:
Hi!

The following testcase ICEs during partial instantiation of structured
binding pack.  tsubst_pack_expansion assumed DECL_VALUE_EXPR
in that case will be TREE_VEC containing what should be expanded.
But when the initializer of the structured bindings is still type dependent,
we know neither the size of the sb pack nor its content, cp_finish_decomp
in that case doesn't set DECL_VALUE_EXPR to a TREE_VEC at all, but to
ARRAY_REF of the base and index within the structured binding (and pack if
any).
tsubst_pack_expansion can set arg_pack to NULL_TREE and in that case
       else
         {
           /* We can't substitute for this parameter pack.  We use a flag as
              well as the missing_level counter because function parameter
              packs don't have a level.  */
           gcc_assert (processing_template_decl || is_auto (parm_pack)
                       || args == NULL_TREE);
           unsubstituted_packs = true;
         }
will trigger and later on in the function unsubstituted_packs is handled
properly.

So, the following patch makes sure to set arg_pack to NULL_TREE in that
case.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-03-13  Jakub Jelinek  <[email protected]>

        PR c++/124456
        * pt.cc (tsubst_pack_expansion): For structured binding pack
        with type dependent pack set arg_pack to NULL_TREE.

        * g++.dg/cpp26/decomp27.C: New test.

--- gcc/cp/pt.cc.jj     2026-03-12 08:48:04.747569066 +0100
+++ gcc/cp/pt.cc        2026-03-12 11:49:59.013257004 +0100
@@ -14135,12 +14135,24 @@ tsubst_pack_expansion (tree t, tree args
            return error_mark_node;
          gcc_assert (DECL_HAS_VALUE_EXPR_P (orig_arg));
          arg_pack = DECL_VALUE_EXPR (orig_arg);
-         tree vec = make_tree_vec (TREE_VEC_LENGTH (arg_pack) - 2);
-         if (TREE_VEC_LENGTH (vec))
-           memcpy (TREE_VEC_BEGIN (vec), &TREE_VEC_ELT (arg_pack, 2),
-                   TREE_VEC_LENGTH (vec) * sizeof (tree));
-         arg_pack = make_node (NONTYPE_ARGUMENT_PACK);
-         ARGUMENT_PACK_ARGS (arg_pack) = vec;
+         if (TREE_CODE (arg_pack) == ARRAY_REF)
+           {
+             /* If the structured binding pack has type dependent
+                base, we can't expand it yet.  */
+             tree base = TREE_OPERAND (arg_pack, 0);
+             gcc_assert (VAR_P (base)
+                         && type_dependent_expression_p (base));
+             arg_pack = NULL_TREE;
+           }
+         else
+           {
+             tree vec = make_tree_vec (TREE_VEC_LENGTH (arg_pack) - 2);
+             if (TREE_VEC_LENGTH (vec))
+               memcpy (TREE_VEC_BEGIN (vec), &TREE_VEC_ELT (arg_pack, 2),
+                       TREE_VEC_LENGTH (vec) * sizeof (tree));
+             arg_pack = make_node (NONTYPE_ARGUMENT_PACK);
+             ARGUMENT_PACK_ARGS (arg_pack) = vec;
+           }

Let's put the TREE_VEC case first, with a pointer to cp_finish_decomp and the comment on the packv variable, since that seems to be where this vec is documented.

OK with that tweeaek.

        }
        else
          {
--- gcc/testsuite/g++.dg/cpp26/decomp27.C.jj    2026-03-12 11:55:52.976313204 
+0100
+++ gcc/testsuite/g++.dg/cpp26/decomp27.C       2026-03-12 12:02:12.991931928 
+0100
@@ -0,0 +1,31 @@
+// PR c++/124456
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+struct A { int i; };
+
+template <typename... T>
+A
+foo (T...)
+{
+  return A ();
+}
+
+template <typename... T>
+int
+bar (T...)
+{
+  return 0;
+}
+
+template <typename... T>
+auto
+baz (T... t)
+{
+  return [=] (auto... a) {
+    auto [... k] = foo (t..., a...);   // { dg-warning "structured binding packs only available 
with" "" { target { c++17 && c++23_down } } }
+    return bar (k...); };              // { dg-warning "structured bindings only available 
with" "" { target c++14_down } .-1 }
+}
+
+auto x = baz (1);
+auto y = x (1, 2, 3);

        Jakub


Reply via email to