This patch implements P0846R0: ADL and Function Templates that are not Visible <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0846r0.html> whereby a name for which a normal lookup produces either no result or finds one or more functions and that is followed by a "<" would be treated as if a function template name had been found and would cause ADL to be performed. Thus e.g.
namespace N { struct S { }; template<int X> void f(S); } void bar (N::S s) { f<3>(s); } will now compile; ADL will find N::f. The gist of the approach I took is in cp_parser_template_name and setting TEMPLATE_ID_TRY_ADL_P. One of the side effects is that a function name followed by a < means that the < is always taken as the delimiter of a template-argument-list, rendering e.g. this fun-ptr < a; ill-formed. There's something I'm not clear on; the proposal talks about an unqualified-id followed by a <, which is also the case for a.foo < 1; which is "postfix-expression . unqualified-id <", but treating "foo" as a template name would break valid programs. I don't think this proposal should be in effect for class member accesses, so I've disabled it by using scoped_p below. See fn-template8.C for a complete testcase for this scenario. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2018-10-28 Marek Polacek <pola...@redhat.com> Implement P0846R0, ADL and function templates. * cp-tree.h (TEMPLATE_ID_TRY_ADL_P): New macro. * decl.c (grokfndecl): Allow FUNCTION_DECL in assert. * parser.c (cp_parser_postfix_expression): Also perform ADL for template-id that is TEMPLATE_ID_TRY_ADL_P. (cp_parser_template_id): Adjust a call to cp_parser_template_name. Allow FUNCTION_DECL in assert. Return error node if parsing the template argument list didn't go well. Set TEMPLATE_ID_TRY_ADL_P on the template-id. (cp_parser_template_name): Add a new parameter. Consider a name to refer to a template if it is an unqualified-id followed by <. (cp_parser_constructor_declarator_p): Adjust a call to cp_parser_template_name. * 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-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/template/pr61745.C: Add target to dg-error. diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h index 26ded3a9214..aa7ddb0830c 100644 --- gcc/cp/cp-tree.h +++ gcc/cp/cp-tree.h @@ -409,6 +409,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; SWITCH_STMT_ALL_CASES_P (in SWITCH_STMT) REINTERPRET_CAST_P (in NOP_EXPR) ALIGNOF_EXPR_STD_P (in ALIGNOF_EXPR) + TEMPLATE_ID_TRY_ADL_P (in TEMPLATE_ID_EXPR) 1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE) TI_PENDING_TEMPLATE_FLAG. TEMPLATE_PARMS_FOR_INLINE. @@ -4987,6 +4988,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define ALIGNOF_EXPR_STD_P(NODE) \ TREE_LANG_FLAG_0 (ALIGNOF_EXPR_CHECK (NODE)) +/* True iff this template-id uses an unqualified-id that should + be considered to refer to a template-name, as per P0846R0. */ +#define TEMPLATE_ID_TRY_ADL_P(NODE) \ + (TREE_LANG_FLAG_0 (TEMPLATE_ID_EXPR_CHECK (NODE))) + /* An enumeration of the kind of tags that C++ accepts. */ enum tag_types { none_type = 0, /* Not a tag type. */ diff --git gcc/cp/decl.c gcc/cp/decl.c index 5ebfaaf85e6..e63743f8a6f 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -8827,7 +8827,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 gcc/cp/parser.c gcc/cp/parser.c index ebe326eb923..a99bde334fb 100644 --- gcc/cp/parser.c +++ gcc/cp/parser.c @@ -2323,7 +2323,7 @@ static tree cp_parser_type_parameter static tree cp_parser_template_id (cp_parser *, bool, bool, enum tag_types, bool); static tree cp_parser_template_name - (cp_parser *, bool, bool, bool, enum tag_types, bool *); + (cp_parser *, bool, bool, bool, enum tag_types, bool *, bool *); static tree cp_parser_template_argument_list (cp_parser *); static tree cp_parser_template_argument @@ -7165,7 +7165,9 @@ 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) + || (TREE_CODE (postfix_expression) == TEMPLATE_ID_EXPR + && TEMPLATE_ID_TRY_ADL_P (postfix_expression))) { if (!args->is_empty ()) { @@ -7175,9 +7177,13 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, = perform_koenig_lookup (postfix_expression, args, complain); } - else + else if (identifier_p (postfix_expression)) postfix_expression = unqualified_fn_lookup_error (postfix_expression); + else + postfix_expression + = unqualified_fn_lookup_error (TREE_OPERAND + (postfix_expression, 0)); } /* We do not perform argument-dependent lookup if normal lookup finds a non-function, in accordance @@ -15848,7 +15854,6 @@ cp_parser_template_id (cp_parser *parser, tree template_id; cp_token_position start_of_id = 0; cp_token *next_token = NULL, *next_token_2 = NULL; - bool is_identifier; /* If the next token corresponds to a template-id, there is no need to reparse it. */ @@ -15877,12 +15882,14 @@ cp_parser_template_id (cp_parser *parser, push_deferring_access_checks (dk_deferred); /* Parse the template-name. */ - is_identifier = false; + bool is_identifier = false; + bool id_as_tmpl_name = false; templ = cp_parser_template_name (parser, template_keyword_p, check_dependency_p, is_declaration, tag_type, - &is_identifier); + &is_identifier, + &id_as_tmpl_name); if (templ == error_mark_node || is_identifier) { pop_deferring_access_checks (); @@ -15951,6 +15958,35 @@ cp_parser_template_id (cp_parser *parser, } /* Parse the arguments. */ arguments = cp_parser_enclosed_template_argument_list (parser); + + if (id_as_tmpl_name + && (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: @@ -15973,9 +16009,12 @@ cp_parser_template_id (cp_parser *parser, template_id = error_mark_node; /* Build a representation of the specialization. */ else if (identifier_p (templ)) - template_id = build_min_nt_loc (combined_loc, - TEMPLATE_ID_EXPR, - templ, arguments); + { + template_id = build_min_nt_loc (combined_loc, + TEMPLATE_ID_EXPR, + templ, arguments); + TEMPLATE_ID_TRY_ADL_P (template_id) = id_as_tmpl_name; + } else if (DECL_TYPE_TEMPLATE_P (templ) || DECL_TEMPLATE_TEMPLATE_PARM_P (templ)) { @@ -16007,6 +16046,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); @@ -16088,7 +16128,9 @@ cp_parser_template_id (cp_parser *parser, name refers to a set of overloaded functions, at least one of which is a template, or an IDENTIFIER_NODE with the name of the template, if TEMPLATE_KEYWORD_P is true. If CHECK_DEPENDENCY_P is FALSE, - names are looked up inside uninstantiated templates. */ + names are looked up inside uninstantiated templates. + ID_AS_TMPL_NAME will be set if we should consider the unqualified-id + as a template-name. */ static tree cp_parser_template_name (cp_parser* parser, @@ -16096,7 +16138,8 @@ cp_parser_template_name (cp_parser* parser, bool check_dependency_p, bool is_declaration, enum tag_types tag_type, - bool *is_identifier) + bool *is_identifier, + bool *id_as_tmpl_name) { tree identifier; tree decl; @@ -16209,6 +16252,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, @@ -16241,6 +16288,33 @@ 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. */ + if (TREE_CODE (decl) == OVERLOAD + /* Name lookup found one function. */ + || TREE_CODE (decl) == FUNCTION_DECL) + { + if (id_as_tmpl_name) + *id_as_tmpl_name = true; + found = true; + } + /* Name lookup found nothing. */ + else if (decl == error_mark_node) + { + if (id_as_tmpl_name) + *id_as_tmpl_name = true; + return identifier; + } + } + if (!found) { /* The name does not name a template. */ @@ -26736,7 +26810,8 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) /*check_dependency_p*/false, /*is_declaration*/false, none_type, - /*is_identifier*/NULL); + /*is_identifier*/NULL, + /*id_as_tmpl_name*/NULL); if (DECL_CLASS_TEMPLATE_P (tmpl) || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) /* It's a deduction guide, return true. */; diff --git gcc/testsuite/g++.dg/addr_builtin-1.C gcc/testsuite/g++.dg/addr_builtin-1.C index e8ba31f994c..0c282b1f8cd 100644 --- gcc/testsuite/g++.dg/addr_builtin-1.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template1.C gcc/testsuite/g++.dg/cpp2a/fn-template1.C index e69de29bb2d..2492d9df7d5 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template1.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template10.C gcc/testsuite/g++.dg/cpp2a/fn-template10.C index e69de29bb2d..c69d48fa9b2 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template10.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template11.C gcc/testsuite/g++.dg/cpp2a/fn-template11.C index e69de29bb2d..1a6b6882900 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template11.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template12.C gcc/testsuite/g++.dg/cpp2a/fn-template12.C index e69de29bb2d..fc72fd00584 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template12.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template13.C gcc/testsuite/g++.dg/cpp2a/fn-template13.C index e69de29bb2d..ece6d152601 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template13.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template2.C gcc/testsuite/g++.dg/cpp2a/fn-template2.C index e69de29bb2d..f974c8c2cf9 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template2.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template3.C gcc/testsuite/g++.dg/cpp2a/fn-template3.C index e69de29bb2d..f801625ab3d 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template3.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template4.C gcc/testsuite/g++.dg/cpp2a/fn-template4.C index e69de29bb2d..9259c2ebf23 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template4.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template5.C gcc/testsuite/g++.dg/cpp2a/fn-template5.C index e69de29bb2d..33477c96746 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template5.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template6.C gcc/testsuite/g++.dg/cpp2a/fn-template6.C index e69de29bb2d..63b2377bc6e 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template6.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template7.C gcc/testsuite/g++.dg/cpp2a/fn-template7.C index e69de29bb2d..d048606c0d6 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template7.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template8.C gcc/testsuite/g++.dg/cpp2a/fn-template8.C index e69de29bb2d..9cd28eed5d0 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template8.C +++ 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 gcc/testsuite/g++.dg/cpp2a/fn-template9.C gcc/testsuite/g++.dg/cpp2a/fn-template9.C index e69de29bb2d..19c960cc936 100644 --- gcc/testsuite/g++.dg/cpp2a/fn-template9.C +++ 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 gcc/testsuite/g++.dg/parse/fn-template1.C gcc/testsuite/g++.dg/parse/fn-template1.C index e69de29bb2d..00f8b4920e1 100644 --- gcc/testsuite/g++.dg/parse/fn-template1.C +++ 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 gcc/testsuite/g++.dg/parse/fn-template2.C gcc/testsuite/g++.dg/parse/fn-template2.C index e69de29bb2d..c56694efb92 100644 --- gcc/testsuite/g++.dg/parse/fn-template2.C +++ 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 gcc/testsuite/g++.dg/template/pr61745.C gcc/testsuite/g++.dg/template/pr61745.C index 0f7c280e52a..da5973e3867 100644 --- gcc/testsuite/g++.dg/template/pr61745.C +++ 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 } } };