On 5/4/22 19:20, Marek Polacek wrote:
On Wed, May 04, 2022 at 05:44:45PM -0400, Jason Merrill wrote:
On 5/4/22 16:03, Marek Polacek wrote:
This patch fixes the second half of 64679.  Here we issue a wrong
"redefinition of 'int x'" for the following:

    struct Bar {
      Bar(int, int, int);
    };

    int x = 1;
    Bar bar(int(x), int(x), int{x}); // #1

cp_parser_parameter_declaration_list does pushdecl every time it sees
a named parameter, so the second "int(x)" causes the error.  That's
premature, since this turns out to be a constructor call after the
third argument!

If the first parameter is parenthesized, we can't push until we've
established we're looking at a function declaration.  Therefore this
could be fixed by some kind of lookahead.  I thought about introducing
a lightweight variant of cp_parser_parameter_declaration_list that would
not have any side effects and would return as soon as it figures out
whether it's looking at a declaration or expression.  Since that would
require fairly nontrivial changes, I wanted something simpler.  Something
like delaying the pushdecl until we've reached the ')' following the
parameter-declaration-clause.  But that doesn't quite cut it: we must
have pushed the parameters before processing a default argument, as in:

    Bar bar(int(a), int(b), int c = sizeof(a));  // valid

I wondered how this would affect

   void f(int (i), decltype(i) j = 42);

interestingly, clang and EDG both reject this, but they accept

   void f(int (i), bool b = true, decltype(i) j = 42);

which suggests a similar implementation strategy.  MSVC accepts both.

Sigh, C++.

So the former would be rejected with this patch because decltype(i)
is parsed as the declspec.  And I can't play any cute games with decltype
because a decltype doesn't necessarily mean a parameter:

struct F {
   F(int, int);
};

void
g ()
{
   int x = 42;

   F v1(int(x), decltype(x)(42));

   F f1(int(i), decltype(i) j = 42);
   F f2(int(i), decltype(i) j);
   F f3(int(i), decltype(i)(j));
   F f4(int(i), decltype(i)(j) = 42);
   F f5(int (i), bool b = true, decltype(i) j = 42);
   F f6(int(i), decltype(x)(x));
}


But I think there's a way out: we could pushdecl the parameters as we
go and only stash when there would be a clash, if parsing tentatively.
And then push the pending parameters only at the end of the clause, solely
to get the redefinition/redeclaration error.  That is:

   Bar b(int(x), int(x), int{x});

would mean:
push x
store x
it's not a decl -> discard it, parse as an expression

   Bar b(int(x), int(x), int);

would mean:
push x
store x
it's a decl -> push pending parameters -> error

And then I don't need to push when about to commit, avoiding the need to
change cp_parser_parameter_declaration.  WDYT?

Sounds good to me.

Jason

Reply via email to