Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/pending_changes/gh-506.txt
Original file line number Diff line number Diff line change
@@ -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<int... Is> void f() {
[](auto) {
[]<int... Js, typename U = int[((Js * Is) * ... * 1) - 2]>() {};
};
}
template void f<2>(); // Previously a spurious array bound error, now okay.
40 changes: 38 additions & 2 deletions src/scope_stk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
/*
Expand All @@ -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
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-506.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 -tused --c++20 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-506.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=2 -tused --c++20 --gn 150200 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-506.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=3 -tused --c++20 --clang_version 220100 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-506.rto/default.4.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=4 -tused --ms_c++20 --microsoft_version 1950 Test_name.c
57 changes: 57 additions & 0 deletions tests/tests/regressions/gh-506.sft.cpp
Original file line number Diff line number Diff line change
@@ -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<int... Is> constexpr auto f() {
return [](auto) {
return []<int... Js, int N = ((Js * Is) * ... * 1)>() {
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<int... Is> void f() {
[](auto) {
[]<int... Js, typename U = int[((Js * Is) * ... * 1) - 2]>() {};
};
}
template void f<2>();
}

namespace inner_pack_first_in_template_parameter_default
{
template<int... Is> constexpr auto f() {
return [](auto) {
return []<int... Js,
typename U = int[((Js * Is) * ... * 1) - 2]>() {
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<int... Is> constexpr auto f() {
return [](auto) {
return []<int... Js,
typename U = int[((Is * Js) * ... * 1) - 2]>() {
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));
}
Loading