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