On Thu, 5 Sep 2024, Jason Merrill wrote: > On 9/5/24 10:54 AM, Patrick Palka wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK > > for trunk/14? > > > > -- >8 -- > > > > A lambda within a default template argument used in some template-id > > may have a smaller template depth than the context of the template-id. > > For example, the lambda in v1's default template argument has template > > depth 1, and in v2's has template depth 2, but the template-ids v1<0> > > and v2<0> which uses these default arguments appear in a depth 3 template > > context. So add_extra_args will ultimately return args with depth 3 -- > > too many args for the lambda, leading to a bogus substitution. > > > > This patch fixes this by trimming the result of add_extra_args to match > > the template depth of the lambda. A new LAMBDA_EXPR_TEMPLATE_DEPTH field > > is added that tracks the template-ness of a lambda; > > > > PR c++/116567 > > > > gcc/cp/ChangeLog: > > > > * pt.cc (tsubst_lambda_expr): For a deferred-substitution lambda, > > trim the augmented template arguments to match the template depth > > of the lambda. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp2a/lambda-targ7.C: New test. > > --- > > gcc/cp/pt.cc | 11 +++++++++ > > gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C | 30 +++++++++++++++++++++++ > > 2 files changed, 41 insertions(+) > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C > > > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > > index 747e627f547..c49a26b4f5e 100644 > > --- a/gcc/cp/pt.cc > > +++ b/gcc/cp/pt.cc > > @@ -19699,6 +19699,17 @@ tsubst_lambda_expr (tree t, tree args, > > tsubst_flags_t complain, tree in_decl) > > LAMBDA_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, complain); > > return t; > > } > > + if (LAMBDA_EXPR_EXTRA_ARGS (t)) > > + { > > + /* If we deferred substitution into this lambda, then it's probably > > from > > "probably" seems wrong, given that it wasn't implemented for this case.
I said "probably" because in e.g. template<int N, auto F = []{}> bool b = true; template<class T> void f() { b<0>; } the lambda context has the same depth as the template-id context. But as you point out, the issue is ultimately related vs unrelated parameters rather than depth. > > > + a context (e.g. default template argument context) which may have > > fewer > > + levels than the current context it's embedded in. Adjust the result > > of > > + add_extra_args accordingly. */ > > Hmm, this looks like a situation of not just fewer levels, but potentially > unrelated levels. "args" here is for f, which shares no template context with > v1. What happens if your templates have non-type template parameters? Indeed before add_extra_args 'args' will be unrelated, but after doing add_extra_args the innermost levels of 'args' will correspond to the lambda's template context, and so using get_innermost_template_args ought to get rid of the unrelated arguments, keeping only the ones relevant to the original lambda context. Here's v2 which clarifies the comment to talk about related parameters rather than differing depth, and extends the testcase to invoke the lambda etc. -- >8 -- Subject: [PATCH] c++: template depth of lambda in default targ [PR116567] PR c++/116567 gcc/cp/ChangeLog: * pt.cc (tsubst_lambda_expr): For a deferred-substitution lambda, trim the augmented template arguments to match the template depth of the lambda. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/lambda-targ7.C: New test. --- gcc/cp/pt.cc | 12 +++++++ gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C | 42 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 747e627f547..e6c10d5bd20 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -19699,6 +19699,18 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) LAMBDA_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, complain); return t; } + if (LAMBDA_EXPR_EXTRA_ARGS (t)) + { + /* If we deferred substitution into this lambda, then its original + context (e.g. default template argument context) might be unrelated + to the current context it's embedded in. After add_extra_args though, + the innermost levels of 'args' will correspond to the lambda context, + so get rid of all unrelated levels. */ + tree ctx_parms = DECL_TEMPLATE_PARMS (DECL_TI_TEMPLATE (oldfn)); + if (generic_lambda_fn_p (oldfn)) + ctx_parms = TREE_CHAIN (ctx_parms); + args = get_innermost_template_args (args, TMPL_PARMS_DEPTH (ctx_parms)); + } tree r = build_lambda_expr (); diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C b/gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C new file mode 100644 index 00000000000..97b42d6ed0c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C @@ -0,0 +1,42 @@ +// PR c++/116567 +// { dg-do compile { target c++20 } } + +struct X { int n; }; + +template<auto N, auto F = []{ return N; }> +auto v1 = F; + +template<auto N, auto F = [](auto y){ return decltype(N){N.n + y}; }> +auto v1g = F; + +template<class T> +struct A { + template<auto N, auto F = []{ return N; }> + static inline auto v2 = F; + + template<auto N, auto F = [](auto y){ return decltype(N){N.n + y}; }> + static inline auto v2g = F; + + template<class U> + struct B { + template<auto N, auto F = []{ return N; }> + static inline auto v3 = F; + + template<auto N, auto F = [](auto y){ return decltype(N){N.n + y}; }> + static inline auto v3g = F; + + template<class V> + static void f() { + static_assert(v1<X{1}>().n == 1); + static_assert(v1g<X{1}>(42).n == 1 + 42); + static_assert(v2<X{2}>().n == 2); + static_assert(v2g<X{2}>(42).n == 2 + 42); + static_assert(v3<X{3}>().n == 3); + static_assert(v3g<X{3}>(42).n == 3 + 42); + } + }; +}; + +int main() { + A<int>::B<int>::f<int>(); +} -- 2.46.0.519.g2e7b89e038