PR c++/60390 * parser.c (cp_parser_member_declaration): Don't allow finish_fully_implicit_template to consider friend declarations to be class member templates. (synthesize_implicit_template_parm): Handling winding back through class scope to the class being defined in order to inject a template argument list.
PR c++/60390 * g++.dg/cpp1y/pr60390.C: New testcase. --- gcc/cp/parser.c | 49 +++++++++++++++++++++++++++++------- gcc/testsuite/g++.dg/cpp1y/pr60390.C | 26 +++++++++++++++++++ 2 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr60390.C diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index a01f204..e0ccec0 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -20521,8 +20521,13 @@ cp_parser_member_declaration (cp_parser* parser) decl = grokfield (declarator, &decl_specifiers, initializer, /*init_const_expr_p=*/true, asm_specification, attributes); - if (parser->fully_implicit_function_template_p) - decl = finish_fully_implicit_template (parser, decl); + if (parser->fully_implicit_function_template_p) + { + if (friend_p) + finish_fully_implicit_template (parser, 0); + else + decl = finish_fully_implicit_template (parser, decl); + } } cp_finalize_omp_declare_simd (parser, decl); @@ -31976,13 +31981,39 @@ synthesize_implicit_template_parm (cp_parser *parser) parent_scope = scope; scope = scope->level_chain; } - if (current_class_type && !LAMBDA_TYPE_P (current_class_type) - && parser->num_classes_being_defined == 0) - while (scope->kind == sk_class) - { - parent_scope = scope; - scope = scope->level_chain; - } + if (current_class_type && !LAMBDA_TYPE_P (current_class_type)) + { + /* If not defining a class, then any class scope is a scope level in + an out-of-line member definition. In this case simply wind back + beyond the first such scope to inject the template argument list. + Otherwise wind back to the class being defined. The latter can + occur in class member friend declarations such as: + + class A { + void foo (auto); + }; + class B { + friend void A::foo (auto); + }; + + The template argument list synthesized for the friend declaration + must be injected between in the scope of 'B', just beyond the scope + of 'A' introduced by 'A::'. */ + + if (parser->num_classes_being_defined == 0) + while (scope->kind == sk_class) + { + parent_scope = scope; + scope = scope->level_chain; + } + else + while (scope->kind == sk_class + && !TYPE_BEING_DEFINED (scope->this_entity)) + { + parent_scope = scope; + scope = scope->level_chain; + } + } current_binding_level = scope; diff --git a/gcc/testsuite/g++.dg/cpp1y/pr60390.C b/gcc/testsuite/g++.dg/cpp1y/pr60390.C new file mode 100644 index 0000000..5cd5539 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr60390.C @@ -0,0 +1,26 @@ +// PR c++/60390 +// { dg-do compile { target c++1y } } +// { dg-options "" } + +struct A +{ + void foo (auto); +}; + +class B +{ + int m; + friend void A::foo (auto); +}; + +void A::foo (auto i) +{ + B b; + b.m = i; +} + +int main () +{ + A a; + a.foo (7); +} -- 1.9.0