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 } }
 };

Reply via email to