diff options
author | Marek Polacek <polacek@redhat.com> | 2018-11-01 22:10:31 +0000 |
---|---|---|
committer | Marek Polacek <mpolacek@gcc.gnu.org> | 2018-11-01 22:10:31 +0000 |
commit | 5d9a0e3b99e31a2167f6b6ab2473feb58f7c77e8 (patch) | |
tree | 37c5dd82ef0aa75d2a14f04e83a976603e23d193 /gcc | |
parent | 3af0c6bce5e4343a6099ba07e9dbfc3288de40a6 (diff) | |
download | gcc-5d9a0e3b99e31a2167f6b6ab2473feb58f7c77e8.zip gcc-5d9a0e3b99e31a2167f6b6ab2473feb58f7c77e8.tar.gz gcc-5d9a0e3b99e31a2167f6b6ab2473feb58f7c77e8.tar.bz2 |
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) <case OVERLOAD>: 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
Diffstat (limited to 'gcc')
27 files changed, 507 insertions, 18 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index b567d65..b27ae1a 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,16 @@ +2018-11-01 Marek Polacek <polacek@redhat.com> + + 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) <case OVERLOAD>: Remove assert. + 2018-11-01 Nathan Sidwell <nathan@acm.org> * cp-tree.h (struct lang_function): Delete x_local_names field. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 23fcf6b0..f0033dd 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 410dfd1..26ec52f 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 206ceb0..d01c924 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<targs>" 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 2dc0cb1..4226d4d 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 f206f8b..98703db 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,28 @@ +2018-11-01 Marek Polacek <polacek@redhat.com> + + 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 <tkoenig@gcc.gnu.org> PR fortran/54613 diff --git a/gcc/testsuite/g++.dg/addr_builtin-1.C b/gcc/testsuite/g++.dg/addr_builtin-1.C index e8ba31f..0c282b1 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 0000000..2492d9d --- /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 <class T> int f(T); + template <class T> int g(T); + template <class T> int h(T); + template <class T> int e(T); +} + +int v = e<N::A>(N::A()); +int x = f<N::A>(N::A()); +int y = g<N::A>(N::A()); +int z = h<N::A>(N::A()); // { dg-error "expected" } + +template<class> +void fn () +{ + int v = e<N::A>(N::A()); + int x = f<N::A>(N::A()); + int y = g<N::A>(N::A()); + int z = h<N::A>(N::A()); // { dg-error "expected" } +} + +void +test () +{ + fn<int>(); +} 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 0000000..c69d48f --- /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 <class T> static int f(T) { return 1; } + template <class T> static int g(T) { return 2; } + template <class T> static int h(T); + template <class T> static int e(T) { return 3; } +} + +int v = e<N::A>(N::A()); +int x = f<N::A>(N::A()); +int y = g<N::A>(N::A()); +int z = h<N::A>(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 0000000..1a6b688 --- /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 0000000..fc72fd0 --- /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<typename T> int foo(T); + template<typename T> int foo(T, T); + template<typename T> int foo(T, T, T); +}; + +template<typename T> +struct W { + template<typename U> T foo(U); + template<typename U> T foo(U, U); + template<typename U> T foo(U, U, U); +}; + +void +test () +{ + S s; + s.foo<int>(1); + s.foo<int>(1, 2); + s.foo<int>(1, 2, 3); + + W<int> w; + w.foo<int>(1); + w.foo<int>(1, 2); + w.foo<int>(1, 2, 3); + + w.nothere<int>(1); // { dg-error "has no member|expected" } + s.nothere<int>(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 0000000..ece6d15 --- /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<typename T> + 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<int>); + if (i != 3) + __builtin_abort (); + i = baz (a, &A::foo<int>); + 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 0000000..96d9267 --- /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<typename> struct B +{ + template<typename> int foo() { return 0; } + int i = foo<int>(); +}; 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 0000000..20e4801 --- /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> +class A { + template <bool> void b(); + void m_fn1(); +}; + +template <class T> +void A<T>::m_fn1() { b<>(0); } + + +template <int> struct X { + X() { fn<>(0); } + template <int> void fn(); +}; + + +template <typename> void a() { a<int>; } 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 0000000..becaff1 --- /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<int> { }; // { 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<b>; // { dg-error "" } + a = foo<b>(; // { dg-error "expected" } + a = foo<b>(1; // { dg-error "expected" } + a = foo<b>(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 0000000..f974c8c --- /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 0000000..f801625 --- /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 <class T> int f(T) { return 1; } + template <class T> int g(T) { return 2; } + template <class T> int e(T) { return 3; } +} + +int +main () +{ + int v = e<N::A>(N::A()); + if (v != 3) + __builtin_abort (); + int x = f<N::A>(N::A()); + if (x != 1) + __builtin_abort (); + int y = g<N::A>(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 0000000..9259c2e --- /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 <typename T> void foo() { } +template <typename T> void bar(int) { } +int main() +{ + foo<float>(); + bar<int>(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 0000000..33477c9 --- /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 <class T> int f(T) { return 1; } + template <class T> int g(T) { return 2; } + template <class T> 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 0000000..63b2377 --- /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<class> +struct X { + int first = 0; +}; + +int +f () +{ + X<int> 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 0000000..d048606 --- /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<int> { }; // { 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<b>; // { dg-error "after template-argument-list|invalid" } + a = foo<b>(; // { dg-error "expected" } + a = foo<b>(1; // { dg-error "expected" } + a = foo<b>(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 0000000..9cd28ee --- /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<class T> struct S { int n; }; + +template <class T> +int fn1 (S<T>* s) +{ + int i = 1; + return s->n < arr[i + 1]; +} + +template <class T> +int fn2 (S<T> s) +{ + int i = 1; + return s.n < arr[i + 1]; +} + +template <class T> +int fn3 (S<T>* s) +{ + int i = 1; + return s->template n < 1; // { dg-error "parse error in template argument list" } +} + +template <class T> +int fn4 (S<T> 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 0000000..19c960c --- /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<int X> void f(S); +} + +namespace N2 { + template<class T> 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 0000000..00f8b49 --- /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 0000000..c56694e --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/fn-template2.C @@ -0,0 +1,17 @@ +// P0846R0 +// { dg-do compile } + +namespace N1 { + struct S {}; + template<int X> void f(S); +} + +namespace N2 { + template<class T> 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 dc1a673..fba4f6d 100644 --- a/gcc/testsuite/g++.dg/parse/template19.C +++ b/gcc/testsuite/g++.dg/parse/template19.C @@ -6,6 +6,6 @@ template<int> struct A { template<int> 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 0f7c280..da5973e 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<INT,P> operator- <>(const Zp<INT,P>& a, const Zp<INT,P>& b); // { dg-error "declaration|expected" } + // In C++2A, we have an unqualified-id (operator-) followed by + // '<', and name lookup found a function. + friend Zp<INT,P> operator- <>(const Zp<INT,P>& a, const Zp<INT,P>& b); // { dg-error "declaration|expected" "" { target c++17_down } } }; |