So I still don't have anything better.
Another thing is that in "-> decltype(j)" we don't have the right
current_function_decl yet, so I've added the in_lambda_declarator_p flag
to be used in finish_decltype_type so that we know this decltype appertains
to a lambda -- then current_lambda_expr should give us the right lambda,
which has another new flag tracking whether mutable was seen.
The flag to finish_decltype_type seems unneeded; we should be able to tell
from the proxy that it belongs to a lambda. And I would think that the new
handling in finish_decltype_type seems right in general; always refer to
current_lambda_expr instead of current_function_decl, etc.
Good point. I've removed the flag and simplified the patch quite a bit.
However:
- to honor [expr.prim.id.unqual]/4, I have to know if the decltype is
in the lambda's parameter-declaration-clause or not:
[=]() -> decltype((x)) // float const&
[=](decltype((x)) y) // float&
so I'm using LAMBDA_EXPR_CONST_QUAL_P for that.
Makes sense.
- if we want to handle nested lambdas correctly:
[=](decltype((x)) y) {} // float&
[=] {
[](decltype((x)) y) {}; // float const&
}
we probably will need a new flag for decltype.
Hmm? Since the inner lambda has no capture-default, it doesn't qualify
under https://eel.is/c++draft/expr#prim.id.unqual-4.3 , so we look to the
outer lambda instead.
I would believe that we need to improve finish_decltype_type to handle this
properly (see also PR112926) but I don't see the need for a decltype flag.
I'll come back to this later.
@@ -3351,8 +3351,12 @@ check_local_shadow (tree decl)
}
/* Don't complain if it's from an enclosing function. */
else if (DECL_CONTEXT (old) == current_function_decl
- && TREE_CODE (decl) != PARM_DECL
- && TREE_CODE (old) == PARM_DECL)
+ && ((TREE_CODE (decl) != PARM_DECL
+ && TREE_CODE (old) == PARM_DECL)
+ || (is_capture_proxy (old)
+ && current_lambda_expr ()
+ && DECL_CONTEXT (old)
+ == lambda_function (current_lambda_expr ()))))
What case is this handling? Doesn't the previous if already deal with
parm/capture collision?
The proposal says that
[x=1]{ int x; }
is invalid, so I wanted to give an error for it. But since -Wshadow
warns for the case above, I've dropped that hunk and that simplifies
the patch even more.
It is good to give an error, not just a -Wshadow warning; now that I
understand the case you're trying to handle the above hunk makes more sense,
just please add a comment. But for this case you shouldn't need to handle
proxies before the function body, so DECL_CONTEXT == current_function_decl
should be enough?
Ok, I've put it back. And yes -- I don't have to check current_lambda_expr!
So it's simpler now.
Here's what I have now.
-- >8 --
This patch is an attempt to implement P2036R3 along with P2579R0, fixing
build breakages caused by P2036R3.
The simplest example is:
auto counter1 = [j=0]() mutable -> decltype(j) {
return j++;
};
which currently doesn't compile because the 'j' in the capture isn't
visible in the trailing return type. With these proposals, the 'j'
will be in a lambda scope which spans the trailing return type, so
this test will compile.
This oughtn't be difficult but decltype and other issues made this patch
much more challenging.
We have to push the explicit captures before going into _lambda_declarator_opt
because that is what parses the trailing return type. Yet we can't build
any captures until after _lambda_body -> start_lambda_function which
creates the lambda's operator(), without which we can't build a proxy,
but _lambda_body happens only after parsing the declarator. This patch
works around it by creating a fake operator() in make_dummy_lambda_op.
Another thing is that in "-> decltype(j)" we don't have the right
current_function_decl yet. If current_lambda_expr gives us a lambda,
we know this decltype appertains to a lambda. But we have to know if we
are in a parameter-declaration-clause: as per [expr.prim.id.unqual]/4.4,
if we are, we shouldn't be adding "const". The new LAMBDA_EXPR_CONST_QUAL_P
flag tracks this. But it doesn't handle nested lambdas yet, specifically,
[expr.prim.id.unqual]/14.
I don't think this patch changes behavior for the tests in
"capture-default with [=]" as the paper promises; clang++ behaves the
same as gcc with this patch.
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 0d9ed2ea82b..d238ecc3432 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11868,6 +11868,14 @@ cp_parser_lambda_expression (cp_parser* parser)
if (cp_parser_start_tentative_firewall (parser))
start = token;
+ /* A lambda scope starts immediately after the lambda-introducer of E
+ and extends to the end of the compound-statement of E. */
+ begin_scope (sk_lambda, NULL_TREE);
+
+ /* Inject the captures for the sake of the possible
+ trailing-return-type -- we have to be able to look them up. */
This comment seems misleading since they now are in scope throughout the
lambda declarator.
+ push_capture_proxies (lambda_expr);
+
ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr);
if (ok && cp_parser_error_occurred (parser))
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 86b09049677..4d07a00f89a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4514,6 +4514,14 @@ outer_automatic_var_p (tree decl)
&& !TREE_STATIC (decl));
}
+/* Returns true iff DECL is an automatic variable. */
+
+static bool
+automatic_var_p (tree decl)
+{
+ return VAR_P (decl) && !TREE_STATIC (decl);
+}
+
/* DECL satisfies outer_automatic_var_p. Possibly complain about it or
rewrite it for lambda capture.
@@ -12888,9 +12896,12 @@ finish_decltype_type (tree expr, bool
id_expression_or_member_access_p,
}
else
{
- if (outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
- && current_function_decl
- && LAMBDA_FUNCTION_P (current_function_decl))
+ const bool cfun_is_lambda_p
+ = (current_function_decl && LAMBDA_FUNCTION_P (current_function_decl));
+ if ((outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
+ && cfun_is_lambda_p)
+ || (automatic_var_p (STRIP_REFERENCE_REF (expr))
+ && current_lambda_expr ()))
Can we make outer_automatic_var_p give the right answer in this context
instead of adding a separate automatic_var_p, and then just change the
current_function_decl test to current_lambda_expr ()?
What value does cfun_is_lambda_p get if we're in a lambda-declarator inside
another lambda?
{
/* [expr.prim.id.unqual]/3: If naming the entity from outside of an
unevaluated operand within S would refer to an entity captured by
@@ -12908,7 +12919,7 @@ finish_decltype_type (tree expr, bool
id_expression_or_member_access_p,
And we don't handle nested lambdas properly, where we need to
consider the outer lambdas as well (PR112926). */
tree decl = STRIP_REFERENCE_REF (expr);
- tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT
(current_function_decl));
+ tree lam = current_lambda_expr ();
tree cap = lookup_name (DECL_NAME (decl), LOOK_where::BLOCK,
LOOK_want::HIDDEN_LAMBDA);
@@ -12924,17 +12935,26 @@ finish_decltype_type (tree expr, bool
id_expression_or_member_access_p,
if (type && !TYPE_REF_P (type))
{
- tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl));
- if (WILDCARD_TYPE_P (non_reference (obtype)))
- /* We don't know what the eventual obtype quals will be. */
- goto dependent;
- auto direct_type = [](tree t){
- if (INDIRECT_TYPE_P (t))
- return TREE_TYPE (t);
- return t;
- };
- int const quals = cp_type_quals (type)
- | cp_type_quals (direct_type (obtype));
+ int quals;
+ if (cfun_is_lambda_p)
+ {
+ tree obtype = TREE_TYPE (DECL_ARGUMENTS
(current_function_decl));
+ if (WILDCARD_TYPE_P (non_reference (obtype)))
+ /* We don't know what the eventual obtype quals will be. */
+ goto dependent;
+ auto direct_type = [](tree t){
+ if (INDIRECT_TYPE_P (t))
+ return TREE_TYPE (t);
+ return t;
+ };
+ quals = (cp_type_quals (type)
+ | cp_type_quals (direct_type (obtype)));
We might change this block to just be for the explicit object parm case, and
handle all other lambdas with LAMBDA_EXPR_CONST_QUAL_P?
+ }
+ else
+ /* We are in the parameter clause, trailing return type, or
+ the requires clause and have no relevant c_f_decl yet. */
+ quals = (LAMBDA_EXPR_CONST_QUAL_P (lam)
+ ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
type = cp_build_qualified_type (type, quals);
type = build_reference_type (type);
}