On Thu, 5 Sep 2024, Jason Merrill wrote:
On 9/5/24 2:28 PM, Patrick Palka wrote:
On Thu, 5 Sep 2024, Jason Merrill wrote:
On 9/5/24 1:26 PM, Patrick Palka wrote:
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.
Will they? The original function of add_extra_args was to reintroduce
outer
args that we weren't able to substitute the last time through
tsubst_lambda_expr. I expect the innermost levels of 'args' to be the
same
before and after.
Hmm, looking at add_extra_args again, I see that whether the EXTRA_ARGS go
on
the outside or the inside depends on whether they're dependent. How does
this
work other than by accident? >.>
It's kind of a happy accident indeed :P In the cases this patch is
concerned with, i.e. a template-id using a default argument containing
a lambda, the extra args will always be considered dependent because
during default template argument coercion we substitute an incomplete
set of targs into the default targ (namely the element corresponding to
the default targ is NULL_TREE), which any_dependent_template_arguments_p
considers dependent. So add_extra_args will reliably put these captured
extra args as the innermost!
I see that the testcases you enabled this code to handle in
r12-2222-g2c699fd29829cd were also about lambda/requires in default template
arguments. Can we detect this case some other way than uses_template_parms?
I think during default template argument coercion we need to set
tf_partial when in a template context. Then build_extra_args should
remember if this flag was set. Then add_extra_args should merge the
extra arguments according to whether the flag was set during deferring.
Can we do the pruning added by this patch in add_extra_args instead of its
caller?
If we're only concerned with the default template argument situation, then
it seems 'extra' will always be a full set of template arguments (even when
the template-id names a template from the current instantiation) so I think
add_extra_args just needs to substitute into 'extra' and not do any additional
pruning.
Incidentally, why is having extra outer levels causing trouble? I thought we
were generally able to safely ignore them.
I thought extra _inner_ levels are safe to ignore generally? If we have
extra outer levels then it causes the inner args to no longer be at the
right level.
For
template<auto N, auto = [] {}>
bool flag = true;
template<typename>
struct core {
template<typename>
auto func() -> bool {
return flag<0>;
}
};
auto main() -> int {
core<int> _;
[[maybe_unused]] bool flag = _.func<int>();
}
we crash during substitution into lambda's 'auto' return type which has
level 2, and 'args' after add_extra_args is {{int},{0, <empty>}}, so
we see non-type argument at level 2 index 0 where a type is expected.
(And if the lambda used N, that substitution would be similarly wrong
as well.)
How does the following look? Bootstrapped and regtested on
x86_64-pc-linux-gnu. This would be too risky to backport, so for 14 I
think we should go with the original patch.
-- >8 --
Subject: [PATCH] c++: deferring partial substitution into lambda [PR116567]
PR c++/116567
gcc/cp/ChangeLog:
* pt.cc (coerce_template_parms): Pass tf_partial when substituting
into a default template argument in a template context.
(build_extra_args): Set TREE_STATIC on the extra args if this is
a partial substitution.
(add_extra_args): Handle deferred partial substitution.
(tsubst_lammda_expr): Check for tf_partial instead of dependence
of args when determining whether to defer substitution.
(tsubst_expr) <case LAMBDA_EXPR>: Remove tf_partial early exit.
gcc/testsuite/ChangeLog:
* g++.dg/cpp2a/lambda-targ7.C: New test.
---
gcc/cp/pt.cc | 33 +++++++++---------
gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C | 42 +++++++++++++++++++++++
2 files changed, 58 insertions(+), 17 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 9195a5274e1..9a9c16a47c2 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -9326,7 +9326,9 @@ coerce_template_parms (tree parms,
{
/* There must be a default arg in this case. */
arg = tsubst_template_arg (TREE_PURPOSE (parm), new_args,
- complain, in_decl);
+ complain | (processing_template_decl
+ ? tf_partial : tf_none),
+ in_decl);
/* The position of the first default template argument,
is also the number of non-defaulted arguments in NEW_INNER_ARGS.
Record that. */
@@ -13572,6 +13574,9 @@ build_extra_args (tree pattern, tree args,
tsubst_flags_t complain)
if (local_specializations)
if (tree locals = extract_local_specs (pattern, complain))
extra = tree_cons (NULL_TREE, extra, locals);
+ if (complain & tf_partial)
+ /* Remember whether this is a partial substitution. */
+ TREE_STATIC (extra) = true;
return extra;
}
@@ -13581,7 +13586,10 @@ build_extra_args (tree pattern, tree args, tsubst_flags_t complain)
tree
add_extra_args (tree extra, tree args, tsubst_flags_t complain, tree in_decl)
{
- if (extra && TREE_CODE (extra) == TREE_LIST)
+ if (!extra)
+ return args;
+
+ if (TREE_CODE (extra) == TREE_LIST)
{
for (tree elt = TREE_CHAIN (extra); elt; elt = TREE_CHAIN (elt))
{
@@ -13599,13 +13607,11 @@ add_extra_args (tree extra, tree args, tsubst_flags_t
complain, tree in_decl)
gcc_assert (!TREE_PURPOSE (extra));
extra = TREE_VALUE (extra);
}
- if (uses_template_parms (extra))
- {
- /* This can happen after dependent substitution into a
- requires-expr or a lambda that uses constexpr if. */
- extra = tsubst_template_args (extra, args, complain, in_decl);
- args = add_outermost_template_args (args, extra);
- }
+ if (TREE_STATIC (extra))
+ /* This is a partial substitution into e.g. a requires-expr or lambda
+ inside a default template argument; we expect 'extra' to be a full
+ set of template arguments for the template context. */
+ args = tsubst_template_args (extra, args, complain, in_decl);
else
args = add_to_template_args (extra, args);