diff --git a/src/pending_changes/gh-506.txt b/src/pending_changes/gh-506.txt new file mode 100644 index 0000000000..336bcc5786 --- /dev/null +++ b/src/pending_changes/gh-506.txt @@ -0,0 +1,14 @@ +[GH #506] +Pack expansions in nested generic-lambda template default arguments + +A default template argument of a nested generic lambda could be rejected when a +pack expansion involving an enclosing pack was incorrectly treated as empty +before the lambda's template arguments were available. Such defaults now +remain dependent until instantiation. For example, with --c++20 -tused: + + template void f() { + [](auto) { + []() {}; + }; + } + template void f<2>(); // Previously a spurious array bound error, now okay. diff --git a/src/scope_stk.c b/src/scope_stk.c index da3c269657..218bfe1cff 100644 --- a/src/scope_stk.c +++ b/src/scope_stk.c @@ -13490,6 +13490,39 @@ template within a real instantiation of a class. } /* is_alias_prototype_in_real_instantiation */ +static a_boolean has_unbound_template_pack(a_pack_expansion_descr_ptr pedp) +/* +Return TRUE if the expansion described by pedp references a template parameter +pack not found in the current or enclosing instantiations. A pack found with +no argument elements is bound and does not cause TRUE to be returned. +*/ +{ + a_boolean result = FALSE; + a_pack_reference_ptr prp; + a_template_param_ptr templ_param_list; + a_template_arg_ptr templ_arg_list; + + get_curr_template_params_and_args(&templ_param_list, &templ_arg_list); + for (prp = pedp->packs_referenced; prp != NULL; prp = prp->next) { + if (prp->kind == prk_template_param) { + uint32_t elements; + a_template_param_ptr tpp; + a_boolean found; + + (void)find_template_arg_for_pack(templ_param_list, templ_arg_list, + prp->symbol, &elements, &tpp, &found, + /*is_rescan=*/FALSE, + /*is_deduction=*/FALSE); + if (!found) { + result = TRUE; + break; + } /* if */ + } /* if */ + } /* for */ + return result; +} /* has_unbound_template_pack */ + + static a_boolean is_pack_instantiation_context( a_pack_expansion_descr_ptr *p_pedp) /* @@ -13502,7 +13535,8 @@ Something is treated as an instantiation context if: only uses enclosing packs (e.g., the declaration of a member template in the instantiation of an enclosing class template). - We are in a generic lambda prototype instantiation inside some real - enclosing instantiation. + enclosing instantiation and have arguments for all referenced template + parameter packs. - We have real template arguments for all referenced packs. In such contexts, return TRUE. If we found a pack expansion, set *p_pedp @@ -13527,7 +13561,9 @@ that *p_pedp is set even when FALSE is returned. result = TRUE; } else if (pedp->uses_any_enclosing_packs && in_generic_lambda_in_real_instantiation()) { - result = TRUE; + /* A nested lambda's template parameter defaults can reference packs + whose prototype arguments have not been created yet. */ + result = !has_unbound_template_pack(pedp); } else if (pedp->uses_only_enclosing_packs) { a_pack_reference_ptr prp; a_template_nesting_depth max_pack_depth = NO_NESTING_DEPTH; diff --git a/tests/tests/regressions/.gh-506.rto/default.1.1.txt b/tests/tests/regressions/.gh-506.rto/default.1.1.txt new file mode 100644 index 0000000000..bac7ff2049 --- /dev/null +++ b/tests/tests/regressions/.gh-506.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 -tused --c++20 Test_name.c diff --git a/tests/tests/regressions/.gh-506.rto/default.2.1.txt b/tests/tests/regressions/.gh-506.rto/default.2.1.txt new file mode 100644 index 0000000000..58f2c7ea0b --- /dev/null +++ b/tests/tests/regressions/.gh-506.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 -tused --c++20 --gn 150200 Test_name.c diff --git a/tests/tests/regressions/.gh-506.rto/default.3.1.txt b/tests/tests/regressions/.gh-506.rto/default.3.1.txt new file mode 100644 index 0000000000..60de6710cb --- /dev/null +++ b/tests/tests/regressions/.gh-506.rto/default.3.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=3 -tused --c++20 --clang_version 220100 Test_name.c diff --git a/tests/tests/regressions/.gh-506.rto/default.4.1.txt b/tests/tests/regressions/.gh-506.rto/default.4.1.txt new file mode 100644 index 0000000000..6c55ae241d --- /dev/null +++ b/tests/tests/regressions/.gh-506.rto/default.4.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=4 -tused --ms_c++20 --microsoft_version 1950 Test_name.c diff --git a/tests/tests/regressions/gh-506.sft.cpp b/tests/tests/regressions/gh-506.sft.cpp new file mode 100644 index 0000000000..354d9d402f --- /dev/null +++ b/tests/tests/regressions/gh-506.sft.cpp @@ -0,0 +1,57 @@ +//type:fp +//options:--c++20:--c++20 --gn 150200:--c++20 --clang_version 220100:--ms_c++20 --microsoft_version 1950 +//options_all:-tused + +namespace nontype_template_parameter_default +{ + template constexpr auto f() { + return [](auto) { + return []() { + return N; + }; + }; + } + + static_assert(f<>()(0).template operator()<>() == 1); + static_assert(f<2, 3>()(0).template operator()<4, 5>() == 120); +} + +namespace uninstantiated_template_parameter_default +{ + template void f() { + [](auto) { + []() {}; + }; + } + template void f<2>(); +} + +namespace inner_pack_first_in_template_parameter_default +{ + template constexpr auto f() { + return [](auto) { + return []() { + return sizeof(U); + }; + }; + } + + static_assert(f<2>()(0).template operator()<3>() == 4 * sizeof(int)); + static_assert(f<2, 3>()(0).template operator()<4, 5>() == 118 * sizeof(int)); +} + +namespace outer_pack_first_in_template_parameter_default +{ + template constexpr auto f() { + return [](auto) { + return []() { + return sizeof(U); + }; + }; + } + + static_assert(f<2>()(0).template operator()<3>() == 4 * sizeof(int)); + static_assert(f<2, 3>()(0).template operator()<4, 5>() == 118 * sizeof(int)); +}