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> (); +}
