From: Marek Polacek Date: Thu, 1 Nov 2018 22:10:31 +0000 (+0000) Subject: Implement P0846R0, ADL and function templates. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5d9a0e3b99e31a2167f6b6ab2473feb58f7c77e8;p=gcc.git Implement P0846R0, ADL and function templates. * decl.c (grokfndecl): Allow FUNCTION_DECL in assert. * lex.c (unqualified_fn_lookup_error): Handle TEMPLATE_ID_EXPR. * parser.c (cp_parser_postfix_expression): Do ADL for a template-name. (cp_parser_template_id): Give errors if parsing the template argument list didn't go well. Allow FUNCTION_DECL in assert. (cp_parser_template_name): Consider a name to refer to a template if it is an unqualified-id followed by a <. Don't return the identifier if the decl is a function and dependent. * pt.c (tsubst_copy) : Remove assert. * g++.dg/addr_builtin-1.C: Adjust dg-error. * g++.dg/cpp2a/fn-template1.C: New test. * g++.dg/cpp2a/fn-template10.C: New test. * g++.dg/cpp2a/fn-template11.C: New test. * g++.dg/cpp2a/fn-template12.C: New test. * g++.dg/cpp2a/fn-template13.C: New test. * g++.dg/cpp2a/fn-template14.C: New test. * g++.dg/cpp2a/fn-template15.C: New test. * g++.dg/cpp2a/fn-template16.C: New test. * g++.dg/cpp2a/fn-template2.C: New test. * g++.dg/cpp2a/fn-template3.C: New test. * g++.dg/cpp2a/fn-template4.C: New test. * g++.dg/cpp2a/fn-template5.C: New test. * g++.dg/cpp2a/fn-template6.C: New test. * g++.dg/cpp2a/fn-template7.C: New test. * g++.dg/cpp2a/fn-template8.C: New test. * g++.dg/cpp2a/fn-template9.C: New test. * g++.dg/parse/fn-template1.C: New test. * g++.dg/parse/fn-template2.C: New test. * g++.dg/parse/template19.C: Adjust dg-error. * g++.dg/template/pr61745.C: Add target to dg-error. From-SVN: r265734 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index b567d6565eb..b27ae1ad703 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,16 @@ +2018-11-01 Marek Polacek + + Implement P0846R0, ADL and function templates. + * decl.c (grokfndecl): Allow FUNCTION_DECL in assert. + * lex.c (unqualified_fn_lookup_error): Handle TEMPLATE_ID_EXPR. + * parser.c (cp_parser_postfix_expression): Do ADL for a template-name. + (cp_parser_template_id): Give errors if parsing the template argument + list didn't go well. Allow FUNCTION_DECL in assert. + (cp_parser_template_name): Consider a name to refer to a template if + it is an unqualified-id followed by a <. Don't return the identifier + if the decl is a function and dependent. + * pt.c (tsubst_copy) : Remove assert. + 2018-11-01 Nathan Sidwell * cp-tree.h (struct lang_function): Delete x_local_names field. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 23fcf6b0471..f0033dd5458 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -8857,7 +8857,9 @@ grokfndecl (tree ctype, the information in the TEMPLATE_ID_EXPR. */ SET_DECL_IMPLICIT_INSTANTIATION (decl); - gcc_assert (identifier_p (fns) || TREE_CODE (fns) == OVERLOAD); + gcc_assert (identifier_p (fns) + || TREE_CODE (fns) == OVERLOAD + || TREE_CODE (fns) == FUNCTION_DECL); DECL_TEMPLATE_INFO (decl) = build_template_info (fns, args); for (t = TYPE_ARG_TYPES (TREE_TYPE (decl)); t; t = TREE_CHAIN (t)) diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index 410dfd1bc5b..26ec52f3498 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -541,6 +541,9 @@ unqualified_fn_lookup_error (cp_expr name_expr) if (loc == UNKNOWN_LOCATION) loc = input_location; + if (TREE_CODE (name) == TEMPLATE_ID_EXPR) + name = TREE_OPERAND (name, 0); + if (processing_template_decl) { /* In a template, it is invalid to write "f()" or "f(3)" if no diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 206ceb048d4..d01c92431ef 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7195,7 +7195,11 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, if (idk == CP_ID_KIND_UNQUALIFIED || idk == CP_ID_KIND_TEMPLATE_ID) { - if (identifier_p (postfix_expression)) + if (identifier_p (postfix_expression) + /* In C++2A, we may need to perform ADL for a template + name. */ + || (TREE_CODE (postfix_expression) == TEMPLATE_ID_EXPR + && identifier_p (TREE_OPERAND (postfix_expression, 0)))) { if (!args->is_empty ()) { @@ -16029,6 +16033,37 @@ cp_parser_template_id (cp_parser *parser, } /* Parse the arguments. */ arguments = cp_parser_enclosed_template_argument_list (parser); + + if ((cxx_dialect > cxx17) + && (TREE_CODE (templ) == FUNCTION_DECL || identifier_p (templ)) + && !template_keyword_p + && (cp_parser_error_occurred (parser) + || cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))) + { + /* This didn't go well. */ + if (TREE_CODE (templ) == FUNCTION_DECL) + { + /* C++2A says that "function-name < a;" is now ill-formed. */ + if (cp_parser_error_occurred (parser)) + { + error_at (token->location, "invalid template-argument-list"); + inform (token->location, "function name as the left hand " + "operand of %<<%> is ill-formed in C++2a; wrap the " + "function name in %<()%>"); + } + else + /* We expect "f" to be followed by "(args)". */ + error_at (cp_lexer_peek_token (parser->lexer)->location, + "expected %<(%> after template-argument-list"); + if (start_of_id) + /* Purge all subsequent tokens. */ + cp_lexer_purge_tokens_after (parser->lexer, start_of_id); + } + else + cp_parser_simulate_error (parser); + pop_deferring_access_checks (); + return error_mark_node; + } } /* Set the location to be of the form: @@ -16085,6 +16120,7 @@ cp_parser_template_id (cp_parser *parser, a function-template. */ gcc_assert ((DECL_FUNCTION_TEMPLATE_P (templ) || TREE_CODE (templ) == OVERLOAD + || TREE_CODE (templ) == FUNCTION_DECL || BASELINK_P (templ))); template_id = lookup_template_function (templ, arguments); @@ -16287,6 +16323,10 @@ cp_parser_template_name (cp_parser* parser, } } + /* cp_parser_lookup_name clears OBJECT_TYPE. */ + const bool scoped_p = ((parser->scope ? parser->scope + : parser->context->object_type) != NULL_TREE); + /* Look up the name. */ decl = cp_parser_lookup_name (parser, identifier, tag_type, @@ -16319,6 +16359,27 @@ cp_parser_template_name (cp_parser* parser, if (TREE_CODE (*iter) == TEMPLATE_DECL) found = true; + if (!found + && (cxx_dialect > cxx17) + && !scoped_p + && cp_lexer_next_token_is (parser->lexer, CPP_LESS)) + { + /* [temp.names] says "A name is also considered to refer to a template + if it is an unqualified-id followed by a < and name lookup finds + either one or more functions or finds nothing." */ + + /* The "more functions" case. Just use the OVERLOAD as normally. + We don't use is_overloaded_fn here to avoid considering + BASELINKs. */ + if (TREE_CODE (decl) == OVERLOAD + /* Name lookup found one function. */ + || TREE_CODE (decl) == FUNCTION_DECL) + found = true; + /* Name lookup found nothing. */ + else if (decl == error_mark_node) + return identifier; + } + if (!found) { /* The name does not name a template. */ @@ -16327,15 +16388,6 @@ cp_parser_template_name (cp_parser* parser, } } - /* If DECL is dependent, and refers to a function, then just return - its name; we will look it up again during template instantiation. */ - if (DECL_FUNCTION_TEMPLATE_P (decl) || !DECL_P (decl)) - { - tree scope = ovl_scope (decl); - if (TYPE_P (scope) && dependent_type_p (scope)) - return identifier; - } - return decl; } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 2dc0cb1629c..4226d4d6b5c 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15538,10 +15538,6 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) return t; case OVERLOAD: - /* An OVERLOAD will always be a non-dependent overload set; an - overload set from function scope will just be represented with an - IDENTIFIER_NODE, and from class scope with a BASELINK. */ - gcc_assert (!uses_template_parms (t)); /* We must have marked any lookups as persistent. */ gcc_assert (!OVL_LOOKUP_P (t) || OVL_USED_P (t)); return t; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f206f8b3878..98703db2305 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,28 @@ +2018-11-01 Marek Polacek + + Implement P0846R0, ADL and function templates. + * g++.dg/addr_builtin-1.C: Adjust dg-error. + * g++.dg/cpp2a/fn-template1.C: New test. + * g++.dg/cpp2a/fn-template10.C: New test. + * g++.dg/cpp2a/fn-template11.C: New test. + * g++.dg/cpp2a/fn-template12.C: New test. + * g++.dg/cpp2a/fn-template13.C: New test. + * g++.dg/cpp2a/fn-template14.C: New test. + * g++.dg/cpp2a/fn-template15.C: New test. + * g++.dg/cpp2a/fn-template16.C: New test. + * g++.dg/cpp2a/fn-template2.C: New test. + * g++.dg/cpp2a/fn-template3.C: New test. + * g++.dg/cpp2a/fn-template4.C: New test. + * g++.dg/cpp2a/fn-template5.C: New test. + * g++.dg/cpp2a/fn-template6.C: New test. + * g++.dg/cpp2a/fn-template7.C: New test. + * g++.dg/cpp2a/fn-template8.C: New test. + * g++.dg/cpp2a/fn-template9.C: New test. + * g++.dg/parse/fn-template1.C: New test. + * g++.dg/parse/fn-template2.C: New test. + * g++.dg/parse/template19.C: Adjust dg-error. + * g++.dg/template/pr61745.C: Add target to dg-error. + 2017-11-01 Thomas Koenig PR fortran/54613 diff --git a/gcc/testsuite/g++.dg/addr_builtin-1.C b/gcc/testsuite/g++.dg/addr_builtin-1.C index e8ba31f994c..0c282b1f8cd 100644 --- a/gcc/testsuite/g++.dg/addr_builtin-1.C +++ b/gcc/testsuite/g++.dg/addr_builtin-1.C @@ -108,7 +108,7 @@ static F* test_taking_address_of_gcc_builtin () a = p - __builtin_trap; // { dg-error "built-in" } // Relational operators. Ill-formed but allowed with -fpermissive. - a = __builtin_trap < p; // { dg-error "built-in" } + a = __builtin_trap < p; // { dg-error "built-in|invalid template-argument-list" } a = p < __builtin_trap; // { dg-error "built-in" } a = __builtin_trap <= p; // { dg-error "built-in" } diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template1.C b/gcc/testsuite/g++.dg/cpp2a/fn-template1.C new file mode 100644 index 00000000000..2492d9df7d5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template1.C @@ -0,0 +1,37 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +int h; +void g(); +void e(); +void e(int); +void e(int, int); + +namespace N { + struct A { }; + template int f(T); + template int g(T); + template int h(T); + template int e(T); +} + +int v = e(N::A()); +int x = f(N::A()); +int y = g(N::A()); +int z = h(N::A()); // { dg-error "expected" } + +template +void fn () +{ + int v = e(N::A()); + int x = f(N::A()); + int y = g(N::A()); + int z = h(N::A()); // { dg-error "expected" } +} + +void +test () +{ + fn(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template10.C b/gcc/testsuite/g++.dg/cpp2a/fn-template10.C new file mode 100644 index 00000000000..c69d48fa9b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template10.C @@ -0,0 +1,22 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +int h; +void g(); +void e(); +void e(int); +void e(int, int); + +namespace N { + struct A { }; + template static int f(T) { return 1; } + template static int g(T) { return 2; } + template static int h(T); + template static int e(T) { return 3; } +} + +int v = e(N::A()); +int x = f(N::A()); +int y = g(N::A()); +int z = h(N::A()); // { dg-error "expected" } diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template11.C b/gcc/testsuite/g++.dg/cpp2a/fn-template11.C new file mode 100644 index 00000000000..1a6b6882900 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template11.C @@ -0,0 +1,11 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +int nonconst (); + +int foo () +{ + return blah < // { dg-error "not declared" } + nonconst (), nonconst (); // { dg-error "call to non-.constexpr. function" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template12.C b/gcc/testsuite/g++.dg/cpp2a/fn-template12.C new file mode 100644 index 00000000000..fc72fd00584 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template12.C @@ -0,0 +1,33 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct S { + template int foo(T); + template int foo(T, T); + template int foo(T, T, T); +}; + +template +struct W { + template T foo(U); + template T foo(U, U); + template T foo(U, U, U); +}; + +void +test () +{ + S s; + s.foo(1); + s.foo(1, 2); + s.foo(1, 2, 3); + + W w; + w.foo(1); + w.foo(1, 2); + w.foo(1, 2, 3); + + w.nothere(1); // { dg-error "has no member|expected" } + s.nothere(1); // { dg-error "has no member|expected" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template13.C b/gcc/testsuite/g++.dg/cpp2a/fn-template13.C new file mode 100644 index 00000000000..ece6d152601 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template13.C @@ -0,0 +1,32 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct A { + template + int foo (T a, T b) { return a + b; } +}; + +int +bar (A* pa, int (A::*pm)(int, int)) +{ + return (pa->*pm)(1, 2); +} + +int +baz (A pa, int (A::*pm)(int, int)) +{ + return (pa.*pm)(1, 2); +} + +int +main () +{ + A a; + int i = bar (&a, &A::foo); + if (i != 3) + __builtin_abort (); + i = baz (a, &A::foo); + if (i != 3) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template14.C b/gcc/testsuite/g++.dg/cpp2a/fn-template14.C new file mode 100644 index 00000000000..96d9267ff16 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template14.C @@ -0,0 +1,9 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template struct B +{ + template int foo() { return 0; } + int i = foo(); +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template15.C b/gcc/testsuite/g++.dg/cpp2a/fn-template15.C new file mode 100644 index 00000000000..20e48014be1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template15.C @@ -0,0 +1,23 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +// Don't get confused by these valid cases. + +template +class A { + template void b(); + void m_fn1(); +}; + +template +void A::m_fn1() { b<>(0); } + + +template struct X { + X() { fn<>(0); } + template void fn(); +}; + + +template void a() { a; } diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template16.C b/gcc/testsuite/g++.dg/cpp2a/fn-template16.C new file mode 100644 index 00000000000..becaff1e3fb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template16.C @@ -0,0 +1,20 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct undeclared { }; // { dg-error "not a class template" } + +int +main () +{ + int foo (); + int foo (int); + int foo (int, int); + int a, b = 10; + a = foo<; // { dg-error "" } + a = foo < b; // { dg-error "" } + a = foo; // { dg-error "" } + a = foo(; // { dg-error "expected" } + a = foo(1; // { dg-error "expected" } + a = foo(1); // { dg-error "no matching" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template2.C b/gcc/testsuite/g++.dg/cpp2a/fn-template2.C new file mode 100644 index 00000000000..f974c8c2cf9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template2.C @@ -0,0 +1,16 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct A { }; +bool operator <(void (*fp)(), A) { return false; } +void f() {} + +int +main () +{ + A a; + f < a; // { dg-error "invalid" } + bool b = f < a; // { dg-error "invalid" } + (f) < a; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template3.C b/gcc/testsuite/g++.dg/cpp2a/fn-template3.C new file mode 100644 index 00000000000..f801625ab3d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template3.C @@ -0,0 +1,29 @@ +// P0846R0 +// { dg-do run } +// { dg-options "-std=c++2a" } + +void g(); +void e(); +void e(int); +void e(int, int); + +namespace N { + struct A { }; + template int f(T) { return 1; } + template int g(T) { return 2; } + template int e(T) { return 3; } +} + +int +main () +{ + int v = e(N::A()); + if (v != 3) + __builtin_abort (); + int x = f(N::A()); + if (x != 1) + __builtin_abort (); + int y = g(N::A()); + if (y != 2) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template4.C b/gcc/testsuite/g++.dg/cpp2a/fn-template4.C new file mode 100644 index 00000000000..9259c2ebf23 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template4.C @@ -0,0 +1,11 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template void foo() { } +template void bar(int) { } +int main() +{ + foo(); + bar(1); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template5.C b/gcc/testsuite/g++.dg/cpp2a/fn-template5.C new file mode 100644 index 00000000000..33477c96746 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template5.C @@ -0,0 +1,32 @@ +// P0846R0 +// { dg-do run } +// { dg-options "-std=c++2a" } + +int g() { return 11; } +int e() { return 12; } +int e(int) { return 13; } +int e(int, int) { return 14; } + +namespace N { + struct A { }; + template int f(T) { return 1; } + template int g(T) { return 2; } + template int e(T) { return 3; } +} + +int +main () +{ + int v = e(1); + if (v != 13) + __builtin_abort (); + int x = e(1, 2); + if (x != 14) + __builtin_abort (); + int y = g(); + if (y != 11) + __builtin_abort (); + int z = e(); + if (z != 12) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template6.C b/gcc/testsuite/g++.dg/cpp2a/fn-template6.C new file mode 100644 index 00000000000..63b2377bc6e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template6.C @@ -0,0 +1,16 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template +struct X { + int first = 0; +}; + +int +f () +{ + X x, y; + bool b = x.first < y.first; + return b; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template7.C b/gcc/testsuite/g++.dg/cpp2a/fn-template7.C new file mode 100644 index 00000000000..d048606c0d6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template7.C @@ -0,0 +1,18 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct undeclared { }; // { dg-error "not a class template" } + +int +main () +{ + int foo (); + int a, b = 10; + a = foo<; // { dg-error "invalid template-argument-list|invalid" } + a = foo < b; // { dg-error "invalid template-argument-list|invalid" } + a = foo; // { dg-error "after template-argument-list|invalid" } + a = foo(; // { dg-error "expected" } + a = foo(1; // { dg-error "expected" } + a = foo(1); // { dg-error "no matching" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template8.C b/gcc/testsuite/g++.dg/cpp2a/fn-template8.C new file mode 100644 index 00000000000..9cd28eed5d0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template8.C @@ -0,0 +1,34 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +const unsigned long arr[10] = { 2 }; +template struct S { int n; }; + +template +int fn1 (S* s) +{ + int i = 1; + return s->n < arr[i + 1]; +} + +template +int fn2 (S s) +{ + int i = 1; + return s.n < arr[i + 1]; +} + +template +int fn3 (S* s) +{ + int i = 1; + return s->template n < 1; // { dg-error "parse error in template argument list" } +} + +template +int fn4 (S s) +{ + int i = 1; + return s.template n < 1; // { dg-error "parse error in template argument list" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template9.C b/gcc/testsuite/g++.dg/cpp2a/fn-template9.C new file mode 100644 index 00000000000..19c960cc936 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template9.C @@ -0,0 +1,21 @@ +// P0846R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +namespace N1 { + struct S {}; + template void f(S); +} + +namespace N2 { + template void f(T t); +} + +void +g (N1::S s) +{ + f<3>(s); + + using N2::f; + f<3>(s); +} diff --git a/gcc/testsuite/g++.dg/parse/fn-template1.C b/gcc/testsuite/g++.dg/parse/fn-template1.C new file mode 100644 index 00000000000..00f8b4920e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/fn-template1.C @@ -0,0 +1,15 @@ +// P0846R0 +// { dg-do compile } + +struct A { }; +bool operator <(void (*fp)(), A) { return false; } +void f() {} + +int +main () +{ + A a; + f < a; // { dg-error "invalid" "" { target c++2a } } + bool b = f < a; // { dg-error "invalid" "" { target c++2a } } + (f) < a; +} diff --git a/gcc/testsuite/g++.dg/parse/fn-template2.C b/gcc/testsuite/g++.dg/parse/fn-template2.C new file mode 100644 index 00000000000..c56694efb92 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/fn-template2.C @@ -0,0 +1,17 @@ +// P0846R0 +// { dg-do compile } + +namespace N1 { + struct S {}; + template void f(S); +} + +namespace N2 { + template void f(T t); +} + +void +g (N1::S s) +{ + f<3>(s); // { dg-error "was not declared" "" { target c++17_down } } +} diff --git a/gcc/testsuite/g++.dg/parse/template19.C b/gcc/testsuite/g++.dg/parse/template19.C index dc1a67334b5..fba4f6d0bf2 100644 --- a/gcc/testsuite/g++.dg/parse/template19.C +++ b/gcc/testsuite/g++.dg/parse/template19.C @@ -6,6 +6,6 @@ template struct A { template void foo() { - foo<0>::; // { dg-error "before" } + foo<0>::; // { dg-error "before|function template-id" } } }; diff --git a/gcc/testsuite/g++.dg/template/pr61745.C b/gcc/testsuite/g++.dg/template/pr61745.C index 0f7c280e52a..da5973e3867 100644 --- a/gcc/testsuite/g++.dg/template/pr61745.C +++ b/gcc/testsuite/g++.dg/template/pr61745.C @@ -18,5 +18,7 @@ public: // this compiles only if the following definition is moved // AFTER the friend declaration Zp operator-() const { return Zp(p-val); } - friend Zp operator- <>(const Zp& a, const Zp& b); // { dg-error "declaration|expected" } + // In C++2A, we have an unqualified-id (operator-) followed by + // '<', and name lookup found a function. + friend Zp operator- <>(const Zp& a, const Zp& b); // { dg-error "declaration|expected" "" { target c++17_down } } };