https://gcc.gnu.org/g:34a47a8b14e2a74d35a65ccc2bd2a166106b9b96

commit r16-7012-g34a47a8b14e2a74d35a65ccc2bd2a166106b9b96
Author: Jakub Jelinek <[email protected]>
Date:   Sat Jan 24 10:36:45 2026 +0100

    c++: Fix ICE on decltype on non-local structured binding inside of a 
template [PR123667]
    
    The following testcases ICE when decltype (x) appears in a template
    where x is a tuple based structured binding from outside of that template
    (one testcase shows the sb in a function and template is a lambda within
    that function, the other shows namespace scope sb referenced from a
    template).
    What I wrote in the comment there is true only for structured bindings
    within the current template function, in that case that structured binding
    indeed has to have DECL_VALUE_EXPR and lookup_decomp_type might return
    NULL or might not and depending on that we should choose if it is
    a tuple based structured binding and return its type or if we should
    return unlowered type of expr.
    But if decltype in a template refers to a structured binding elsewhere,
    it could have been finalized already and determined to be tuple based
    structured binding, so DECL_HAVE_VALUE_EXPR_P can be false in that case.
    In that case, if ptds.saved would be false, we'd just always
    return lookup_decomp_type.  So, for this case the patch allows
    that case in the assert and asserts lookup_decomp_type returned non-NULL.
    
    2026-01-24  Jakub Jelinek  <[email protected]>
    
            PR c++/123667
            * semantics.cc (finish_decltype_type): Allow a structured binding
            for ptds.saved to have DECL_HAS_VALUE_EXPR_P cleared, if it is
            not within current_function_decl, but in that case assert that
            lookup_decomp_type succeeded.
    
            * g++.dg/cpp1z/decomp66.C: New test.
            * g++.dg/cpp1z/decomp67.C: New test.

Diff:
---
 gcc/cp/semantics.cc                   |  8 ++++++--
 gcc/testsuite/g++.dg/cpp1z/decomp66.C | 23 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp1z/decomp67.C | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index af0870ca57d4..c2fd13ee7977 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -13062,12 +13062,16 @@ finish_decltype_type (tree expr, bool 
id_expression_or_member_access_p,
        {
          if (ptds.saved)
            {
-             gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (expr));
+             gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (expr)
+                                  || (DECL_CONTEXT (expr)
+                                      != current_function_decl));
              /* DECL_HAS_VALUE_EXPR_P is always set if
-                processing_template_decl.  If lookup_decomp_type
+                processing_template_decl at least for structured bindings
+                within the template.  If lookup_decomp_type
                 returns non-NULL, it is the tuple case.  */
              if (tree ret = lookup_decomp_type (expr))
                return ret;
+             gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (expr));
            }
          if (DECL_HAS_VALUE_EXPR_P (expr))
            /* Expr is an array or struct subobject proxy, handle
diff --git a/gcc/testsuite/g++.dg/cpp1z/decomp66.C 
b/gcc/testsuite/g++.dg/cpp1z/decomp66.C
new file mode 100644
index 000000000000..39bf8324f2d5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/decomp66.C
@@ -0,0 +1,23 @@
+// PR c++/123667
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A {
+  int i;
+  template <int I> int &get () { return i; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 2; };
+template <int I> struct std::tuple_element <I,A> { using type = int; };
+
+int
+main ()
+{
+  auto [ x, y ] = A { 0 };     // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  [] (auto t) { using z = decltype (x); } (1);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/decomp67.C 
b/gcc/testsuite/g++.dg/cpp1z/decomp67.C
new file mode 100644
index 000000000000..cfc1ac977d2e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/decomp67.C
@@ -0,0 +1,33 @@
+// PR c++/123667
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A {
+  int i;
+  template <int I> int &get () { return i; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 2; };
+template <int I> struct std::tuple_element <I,A> { using type = int; };
+
+auto [ x, y ] = A { 0 };       // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+
+template <int N>
+void
+foo ()
+{
+  using a = decltype (x);
+  auto [ x, y ] = A { 0 };     // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  using b = decltype (x);
+}
+
+void
+bar ()
+{
+  foo <42> ();
+}

Reply via email to