Here we were parsing template <typename T> struct C { friend C(T::fn)(); // this };
wrongly in C++2a since my P0634 patch. T::fn is actually a function declaration, but cp_parser_constructor_declarator_p was thinking it's a constructor. That function actually has code to prevent the compiler from thinking that S (f)(int); is a constructor: it checks if after 'S' there's '(' followed by ')', '...', or a type. If parsing the type succeeds, it looks like a ctor. In this case it succeeded, because T::fn is a qualified name and we are parsing a member-declaration, so CP_PARSER_FLAGS_TYPENAME_OPTIONAL was set. We can either look further and say it's not a constructor if what comes after "(T::fn)" is '('. Or we can prevent cp_parser_type_specifier from assuming a type if friend_p, because while constructors can be friends, they need to use a qualified-id, and at this point we know that the name isn't qualified, so it can't be a valid friend constructor. Bootstrapped/regtested on x86_64-linux, ok for trunk and 9? 2019-05-24 Marek Polacek <pola...@redhat.com> PR c++/90572 - wrong disambiguation in friend declaration. * parser.c (cp_parser_constructor_declarator_p): Don't allow missing typename for friend declarations. * g++.dg/cpp2a/typename16.C: New test. * g++.dg/parse/friend13.C: New test. diff --git gcc/cp/parser.c gcc/cp/parser.c index 1440fdb0d18..08b4105c97f 100644 --- gcc/cp/parser.c +++ gcc/cp/parser.c @@ -27593,9 +27593,19 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags, parser->num_template_parameter_lists = 0; /* Look for the type-specifier. It's not optional, but its typename - might be. */ + might be. Unless this is a friend declaration; we don't want to + treat + + friend S (T::fn)(int); + + as a constructor, but with P0634, we might assume a type when + looking for the type-specifier. It is actually a function named + `T::fn' that takes one parameter (of type `int') and returns a + value of type `S'. Constructors can be friends, but they must + use a qualified name. */ cp_parser_type_specifier (parser, - (flags & ~CP_PARSER_FLAGS_OPTIONAL), + (friend_p ? CP_PARSER_FLAGS_NONE + : (flags & ~CP_PARSER_FLAGS_OPTIONAL)), /*decl_specs=*/NULL, /*is_declarator=*/true, /*declares_class_or_enum=*/NULL, diff --git gcc/testsuite/g++.dg/cpp2a/typename16.C gcc/testsuite/g++.dg/cpp2a/typename16.C new file mode 100644 index 00000000000..7f4242a1dba --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename16.C @@ -0,0 +1,11 @@ +// PR c++/90572 +// { dg-do compile { target c++2a } } + +struct X { X(int); }; + +template<typename T> struct S { + friend X::X(T::t); +}; + +struct W { using t = int; }; +S<W> s; diff --git gcc/testsuite/g++.dg/parse/friend13.C gcc/testsuite/g++.dg/parse/friend13.C new file mode 100644 index 00000000000..d716247a5da --- /dev/null +++ gcc/testsuite/g++.dg/parse/friend13.C @@ -0,0 +1,16 @@ +// PR c++/90572 + +template <typename T> struct C { + friend C(T::fn)(); + friend C(T::fn)(int); + friend C(T::fn)(int, int); +}; + +struct X { }; + +template<typename T> +struct B { + friend X(T::fn)(); + friend X(T::fn)(int); + friend X(T::fn)(int, int); +};