On Thu, Jul 17, 2025 at 05:20:31PM -0400, Jason Merrill wrote:
> On 7/16/25 5:59 PM, Marek Polacek wrote:
> > On Mon, Jul 14, 2025 at 12:52:41PM -0400, Jason Merrill wrote:
> > > On 7/11/25 5:49 PM, Marek Polacek wrote:
> > > > On Thu, Jul 10, 2025 at 02:13:06PM -0400, Jason Merrill wrote:
> > > > > On 7/9/25 4:27 PM, Marek Polacek wrote:
> > > > > > On Tue, Jul 08, 2025 at 12:15:03PM -0400, Jason Merrill wrote:
> > > > > > > On 7/7/25 4:52 PM, Marek Polacek wrote:
> > > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > > > > > > > 
> > > > > > > > -- >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.
> > > > > > > 
> > > > > > > I was thinking that we could build the real operator() earlier, 
> > > > > > > before the
> > > > > > > trailing return type, so that it's there for the above uses, and 
> > > > > > > then splice
> > > > > > > in the trailing return type to the already-built function 
> > > > > > > declaration,
> > > > > > > perhaps with apply_deduced_return_type.
> > > > > > 
> > > > > > Ah, I see what you mean.  But it's not just the return type that we 
> > > > > > don't
> > > > > > have at the point where we have to have the operator(): it's also 
> > > > > > tx_qual,
> > > > > > exception_spec, std_attrs, and trailing_requires_clause.  
> > > > > > Especially the
> > > > > > requires clause seems to be awkward to set post grokmethod; it 
> > > > > > seems I'd
> > > > > > have to replicate the flag_concepts block in grokfndecl?
> > > > > > 
> > > > > > Maybe I could add (by that I mean add it to the lambda via
> > > > > > finish_member_declaration) a bare bones operator() for the purposes 
> > > > > > of
> > > > > > parsing the return type/noexcept/requires, then after parsing them
> > > > > > construct a real operator(), then find a slot of the bare bones 
> > > > > > op(),
> > > > > > and replace it with the complete one.  I'm not sure if that makes 
> > > > > > sense
> > > > > > to do though.
> > > > > 
> > > > > I was hoping to avoid building more than one op().  But really, why 
> > > > > do you
> > > > > need an op() at all for building the proxies?  Could you use
> > > > > build_dummy_object instead of DECL_ARGUMENTS of some fake op()?
> > > > 
> > > > The problem is that we need operator() to be the var's DECL_CONTEXT
> > > > for is_capture_proxy:
> > > > 
> > > >     && LAMBDA_FUNCTION_P (DECL_CONTEXT (decl)));
> > > 
> > > Maybe we could set their DECL_CONTEXT to the closure type and adjust
> > > is_capture_proxy to handle that case as well?
> > 
> > Ah, now I recall why I had so much trouble when I tried that in GCC 15.
> > The problem is that the closure type is not complete at that point, and
> > that causes problems.  E.g., equal2 in lambda-scope3.C has
> > 
> >    return [t = t](const auto& obj) -> decltype(obj == t)
> > 
> > and the incomplete 't' in the decltype is problematic for 
> > cp_build_binary_op.
> 
> Hmm, problematic how?

decay_conversion has complete_type_or_maybe_complain.

> In any case, I guess it's reasonable to build a dummy op(), but building a
> new one for each capture is not; they should all use the same op(). Adding
> it to the closure and then clobbering it later (perhaps by getting
> duplicate_decls to accept them as duplicates?) seems like a fine way to do
> that.

Ah I meant to get back to this and never did.  We certainly don't need
a separate op() for each capture, but do you mean you want one op() for
each closure?

> See also a few comments below.

[ Thanks, I'll get to it this Sun/Mon. ]

> > 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);
> >         }
> 

Marek

Reply via email to