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? Marek