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

Reply via email to