diff mbox series

c++: template keyword in a typename-specifier [PR94057]

Message ID 20200319212801.2726275-1-polacek@redhat.com
State New
Headers show
Series c++: template keyword in a typename-specifier [PR94057] | expand

Commit Message

Li, Pan2 via Gcc-patches March 19, 2020, 9:28 p.m. UTC
Consider

  template <typename T> class A {
    template <typename U> class B {
      void fn(typename A<T>::B<U>);
    };
  };

which is rejected with
error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
whereas clang/icc/msvc accept it.

"typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
don't mention it anywhere, because the typename-specifier wasn't in C++11;
it was only added to the language in N1376.  Instead, we handle it as
an elaborated-type-specifier (not a problem thus far).   So we get to
cp_parser_nested_name_specifier_opt which has a loop that breaks if we
don't see a < or ::, but that means we can -- tentatively -- parse even
B<U> which is not a nested-name-specifier (it doesn't end with a ::).

Even though we're parsing B<U> tentatively, we issue an error in
cp_parser_class_name -> make_typename_type, but here we should not.  In
fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
cp_parser_class_name hunk.

I think this should compile because [temp.names]/4 says: "In a qualified-id
used as the name in a typename-specifier, elaborated-type-specifier,
using-declaration, or class-or-decltype, an optional keyword template
appearing at the top level is ignored.", added in DR 1710.  Also see
DR 1812.

This issue on its own is not a significant problem or a regression.
However, in C++20, the typename here becomes optional, and so this test
is rejected in C++20, but accepted in C++17:

  template <typename T> class A {
    template <typename U> class B {
      void fn(A<T>::B<U>);
    };
  };

Here we morph A<T>::B<U> into a typename-specifier, but that happens
in cp_parser_simple_type_specifier and we never handle it as above.
To fake the template keyword I'm afraid we need to use cp_parser_template_id
with template_keyword_p=true as in the patch below.  The tricky thing
is to avoid breaking concepts.

Does this approach make sense?  Should these tests be accepted because
of DR 1710 or am I off base here?

Apologies for the verbosity, but I felt it necessary.

Bootstrapped/regtested on x86_64-linux, built Boost/cmcstl2, ok for trunk?

	PR c++/94057 - template keyword in a typename-specifier.
	* parser.c (cp_parser_simple_type_specifier): Assume that a <
	following a qualified-id in a typename-specifier begins
	a template argument list.
	(cp_parser_class_name): Complain only if not parsing tentatively.

	* g++.dg/template/dependent-name5.C: Update dg-error.
	* g++.dg/template/dependent-name7.C: New test.
	* g++.dg/template/dependent-name8.C: New test.
	* g++.dg/template/dependent-name9.C: New test.
---
 gcc/cp/parser.c                               | 32 +++++++++++++++++--
 .../g++.dg/template/dependent-name5.C         |  2 --
 .../g++.dg/template/dependent-name7.C         |  9 ++++++
 .../g++.dg/template/dependent-name8.C         |  9 ++++++
 .../g++.dg/template/dependent-name9.C         |  9 ++++++
 5 files changed, 57 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C


base-commit: f22712bd8a2ed57d3cc7e6fa92730bd5852e27b3

Comments

Li, Pan2 via Gcc-patches March 19, 2020, 9:40 p.m. UTC | #1
On 3/19/20 5:28 PM, Marek Polacek wrote:
> Consider
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(typename A<T>::B<U>);
>      };
>    };
> 
> which is rejected with
> error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
> whereas clang/icc/msvc accept it.
> 
> "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
> don't mention it anywhere, because the typename-specifier wasn't in C++11;
> it was only added to the language in N1376.  Instead, we handle it as
> an elaborated-type-specifier (not a problem thus far).   So we get to
> cp_parser_nested_name_specifier_opt which has a loop that breaks if we
> don't see a < or ::, but that means we can -- tentatively -- parse even
> B<U> which is not a nested-name-specifier (it doesn't end with a ::).
> 
> Even though we're parsing B<U> tentatively, we issue an error in
> cp_parser_class_name -> make_typename_type, but here we should not.  In
> fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
> cp_parser_class_name hunk.
> 
> I think this should compile because [temp.names]/4 says: "In a qualified-id
> used as the name in a typename-specifier, elaborated-type-specifier,
> using-declaration, or class-or-decltype, an optional keyword template
> appearing at the top level is ignored.", added in DR 1710.  Also see
> DR 1812.

Looks good, but please add tests for the other contexts mentioned in 
that passage.

> This issue on its own is not a significant problem or a regression.
> However, in C++20, the typename here becomes optional, and so this test
> is rejected in C++20, but accepted in C++17:
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(A<T>::B<U>);
>      };
>    };
> 
> Here we morph A<T>::B<U> into a typename-specifier, but that happens
> in cp_parser_simple_type_specifier and we never handle it as above.
> To fake the template keyword I'm afraid we need to use cp_parser_template_id
> with template_keyword_p=true as in the patch below.  The tricky thing
> is to avoid breaking concepts.
> 
> Does this approach make sense?  Should these tests be accepted because
> of DR 1710 or am I off base here?
> 
> Apologies for the verbosity, but I felt it necessary.

Verbosity isn't a problem.  :)

> Bootstrapped/regtested on x86_64-linux, built Boost/cmcstl2, ok for trunk?
> 
> 	PR c++/94057 - template keyword in a typename-specifier.
> 	* parser.c (cp_parser_simple_type_specifier): Assume that a <
> 	following a qualified-id in a typename-specifier begins
> 	a template argument list.
> 	(cp_parser_class_name): Complain only if not parsing tentatively.
> 
> 	* g++.dg/template/dependent-name5.C: Update dg-error.
> 	* g++.dg/template/dependent-name7.C: New test.
> 	* g++.dg/template/dependent-name8.C: New test.
> 	* g++.dg/template/dependent-name9.C: New test.
> ---
>   gcc/cp/parser.c                               | 32 +++++++++++++++++--
>   .../g++.dg/template/dependent-name5.C         |  2 --
>   .../g++.dg/template/dependent-name7.C         |  9 ++++++
>   .../g++.dg/template/dependent-name8.C         |  9 ++++++
>   .../g++.dg/template/dependent-name9.C         |  9 ++++++
>   5 files changed, 57 insertions(+), 4 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index cbd5510a8fb..f4175955992 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -18113,6 +18113,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>   		}
>   	    }
>   	}
> +      /* DR 1812: A < following a qualified-id in a typename-specifier
> +	 could safely be assumed to begin a template argument list, so
> +	 the template keyword should be optional.  */
> +      else if (parser->scope
> +	       && qualified_p
> +	       && typename_p
> +	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
> +	{
> +	  cp_parser_parse_tentatively (parser);
> +
> +	  type = cp_parser_template_id (parser,
> +					/*template_keyword_p=*/true,
> +					/*check_dependency_p=*/true,
> +					none_type,
> +					/*is_declaration=*/false);
> +	  /* This is handled below, so back off.  */
> +	  if (type && concept_check_p (type))
> +	    cp_parser_simulate_error (parser);
> +
> +	  if (!cp_parser_parse_definitely (parser))
> +	    type = NULL_TREE;
> +	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
> +	    type = make_typename_type (parser->scope, type, typename_type,
> +				       /*complain=*/tf_error);
> +	  else if (TREE_CODE (type) != TYPE_DECL)
> +	    type = NULL_TREE;
> +	}
>   
>         /* Otherwise, look for a type-name.  */
>         if (!type)
> @@ -23636,8 +23663,9 @@ cp_parser_class_name (cp_parser *parser,
>         && decl != error_mark_node
>         && !is_overloaded_fn (decl))
>       {
> -      decl = make_typename_type (scope, decl, typename_type,
> -				 /*complain=*/tf_error);
> +      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
> +				 ? tf_none : tf_error);
> +      decl = make_typename_type (scope, decl, typename_type, complain);
>         if (decl != error_mark_node)
>   	decl = TYPE_NAME (decl);
>       }
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
> index fc78983324b..15c1acb0347 100644
> --- a/gcc/testsuite/g++.dg/template/dependent-name5.C
> +++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
> @@ -22,9 +22,7 @@ struct A
>   
>     typedef N<int>       type6;
>     typedef A::N<int>    type7;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T>::N<int> type8;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
>     typedef typename A<T*>::template N<int> type10;
>   
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
> new file mode 100644
> index 00000000000..3dfa42d2df0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(A<T>::B<U>&);
> +    void fn(A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
> new file mode 100644
> index 00000000000..ad9e44f9b85
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::B<U>&);
> +    void fn(typename A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
> new file mode 100644
> index 00000000000..6dfdbc176c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::template B<U>&);
> +    void fn(typename A<T>::template B<U>);
> +  };
> +};
> 
> base-commit: f22712bd8a2ed57d3cc7e6fa92730bd5852e27b3
>
Li, Pan2 via Gcc-patches March 20, 2020, 5:06 p.m. UTC | #2
On Thu, Mar 19, 2020 at 05:40:01PM -0400, Jason Merrill wrote:
> On 3/19/20 5:28 PM, Marek Polacek wrote:
> > Consider
> > 
> >    template <typename T> class A {
> >      template <typename U> class B {
> >        void fn(typename A<T>::B<U>);
> >      };
> >    };
> > 
> > which is rejected with
> > error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
> > whereas clang/icc/msvc accept it.
> > 
> > "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
> > don't mention it anywhere, because the typename-specifier wasn't in C++11;
> > it was only added to the language in N1376.  Instead, we handle it as
> > an elaborated-type-specifier (not a problem thus far).   So we get to
> > cp_parser_nested_name_specifier_opt which has a loop that breaks if we
> > don't see a < or ::, but that means we can -- tentatively -- parse even
> > B<U> which is not a nested-name-specifier (it doesn't end with a ::).
> > 
> > Even though we're parsing B<U> tentatively, we issue an error in
> > cp_parser_class_name -> make_typename_type, but here we should not.  In
> > fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
> > cp_parser_class_name hunk.
> > 
> > I think this should compile because [temp.names]/4 says: "In a qualified-id
> > used as the name in a typename-specifier, elaborated-type-specifier,
> > using-declaration, or class-or-decltype, an optional keyword template
> > appearing at the top level is ignored.", added in DR 1710.  Also see
> > DR 1812.
> 
> Looks good, but please add tests for the other contexts mentioned in that
> passage.

Wonderful.  I've added a bunch of tests, and some from the related DRs too.
But I had a problem with the class-or-decltype case: if we have

template<typename T> struct D : T::template B<int>::template C<int> {};

then we still require all the 'template' keywords here (as does clang).  So I'm
kind of confused, but I don't think it's a big deal right now.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

-- >8 --
Consider

  template <typename T> class A {
    template <typename U> class B {
      void fn(typename A<T>::B<U>);
    };
  };

which is rejected with
error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
whereas clang/icc/msvc accept it.

"typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
don't mention it anywhere, because the typename-specifier wasn't in C++11;
it was only added to the language in N1376.  Instead, we handle it as
an elaborated-type-specifier (not a problem thus far).   So we get to
cp_parser_nested_name_specifier_opt which has a loop that breaks if we
don't see a < or ::, but that means we can -- tentatively -- parse even
B<U> which is not a nested-name-specifier (it doesn't end with a ::).

Even though we're parsing B<U> tentatively, we issue an error in
cp_parser_class_name -> make_typename_type, but here we should not.  In
fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
cp_parser_class_name hunk.

I think this should compile because [temp.names]/4 says: "In a qualified-id
used as the name in a typename-specifier, elaborated-type-specifier,
using-declaration, or class-or-decltype, an optional keyword template
appearing at the top level is ignored.", added in DR 1710.  Also see
DR 1812.

This issue on its own is not a significant problem or a regression.
However, in C++20, the typename here becomes optional, and so this test
is rejected in C++20, but accepted in C++17:

  template <typename T> class A {
    template <typename U> class B {
      void fn(A<T>::B<U>);
    };
  };

Here we morph A<T>::B<U> into a typename-specifier, but that happens
in cp_parser_simple_type_specifier and we never handle it as above.
To fake the template keyword I'm afraid we need to use cp_parser_template_id
with template_keyword_p=true as in the patch below.  The tricky thing
is to avoid breaking concepts.

	DR 1710
	PR c++/94057 - template keyword in a typename-specifier.
	* parser.c (cp_parser_simple_type_specifier): Assume that a <
	following a qualified-id in a typename-specifier begins
	a template argument list.
	(cp_parser_class_name): Complain only if not parsing tentatively.

	* g++.dg/template/dependent-name5.C: Update dg-error.
	* g++.dg/template/dependent-name7.C: New test.
	* g++.dg/template/dependent-name8.C: New test.
	* g++.dg/template/dependent-name9.C: New test.
	* g++.dg/template/dependent-name10.C: New test.
	* g++.dg/template/dependent-name11.C: New test.
	* g++.dg/template/dr1794.C: New test.
	* g++.dg/template/dr314.C: New test.
	* g++.dg/template/dr1710.C: New test.
---
 gcc/cp/parser.c                               | 32 +++++++++++++++++--
 .../g++.dg/template/dependent-name10.C        | 18 +++++++++++
 .../g++.dg/template/dependent-name11.C        | 15 +++++++++
 .../g++.dg/template/dependent-name5.C         |  2 --
 .../g++.dg/template/dependent-name7.C         |  9 ++++++
 .../g++.dg/template/dependent-name8.C         |  9 ++++++
 .../g++.dg/template/dependent-name9.C         |  9 ++++++
 gcc/testsuite/g++.dg/template/dr1710.C        |  4 +++
 gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++++++
 gcc/testsuite/g++.dg/template/dr314.C         | 11 +++++++
 10 files changed, 119 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr314.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..f4175955992 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18113,6 +18113,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 		}
 	    }
 	}
+      /* DR 1812: A < following a qualified-id in a typename-specifier
+	 could safely be assumed to begin a template argument list, so
+	 the template keyword should be optional.  */
+      else if (parser->scope
+	       && qualified_p
+	       && typename_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
+	{
+	  cp_parser_parse_tentatively (parser);
+
+	  type = cp_parser_template_id (parser,
+					/*template_keyword_p=*/true,
+					/*check_dependency_p=*/true,
+					none_type,
+					/*is_declaration=*/false);
+	  /* This is handled below, so back off.  */
+	  if (type && concept_check_p (type))
+	    cp_parser_simulate_error (parser);
+
+	  if (!cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
+	    type = make_typename_type (parser->scope, type, typename_type,
+				       /*complain=*/tf_error);
+	  else if (TREE_CODE (type) != TYPE_DECL)
+	    type = NULL_TREE;
+	}
 
       /* Otherwise, look for a type-name.  */
       if (!type)
@@ -23636,8 +23663,9 @@ cp_parser_class_name (cp_parser *parser,
       && decl != error_mark_node
       && !is_overloaded_fn (decl))
     {
-      decl = make_typename_type (scope, decl, typename_type,
-				 /*complain=*/tf_error);
+      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
+				 ? tf_none : tf_error);
+      decl = make_typename_type (scope, decl, typename_type, complain);
       if (decl != error_mark_node)
 	decl = TYPE_NAME (decl);
     }
diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
new file mode 100644
index 00000000000..18e024f7e6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
@@ -0,0 +1,18 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    typedef int TT;
+    typedef int TT2;
+    typedef int TT3;
+    typedef int TT4;
+  };
+};
+
+struct X : A<int>::B<int> {
+  using A<int>::template B<int>::TT;
+  using typename A<int>::template B<int>::TT2;
+  using A<int>::B<int>::TT3;
+  using typename A<int>::B<int>::TT4;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
new file mode 100644
index 00000000000..687a9bd5df5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
@@ -0,0 +1,15 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U>
+  struct W { };
+};
+
+void
+g ()
+{
+  // class-key nested-name-specifier template[opt] simple-template-id
+  struct A<int>::W<int> w;
+  struct A<int>::template W<int> w2;
+}
diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
index fc78983324b..15c1acb0347 100644
--- a/gcc/testsuite/g++.dg/template/dependent-name5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -22,9 +22,7 @@ struct A
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
new file mode 100644
index 00000000000..3dfa42d2df0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(A<T>::B<U>&);
+    void fn(A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
new file mode 100644
index 00000000000..ad9e44f9b85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::B<U>&);
+    void fn(typename A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
new file mode 100644
index 00000000000..6dfdbc176c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::template B<U>&);
+    void fn(typename A<T>::template B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
new file mode 100644
index 00000000000..fbbccde9bbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710.C
@@ -0,0 +1,4 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct D : T::template B<int>::template C<int> {};
diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
new file mode 100644
index 00000000000..f629d7d0b98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1794.C
@@ -0,0 +1,14 @@
+// DR 1794 - template keyword and alias templates.
+// { dg-do compile { target c++11 } }
+
+template<template<typename> class Template>
+struct Internal {
+  template<typename Arg>
+  using Bind = Template<Arg>;
+};
+
+template<template<typename> class Template, typename Arg>
+using Instantiate = Template<Arg>;
+
+template<template<typename> class Template, typename Argument>
+using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
new file mode 100644
index 00000000000..e3c9c9e5a0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr314.C
@@ -0,0 +1,11 @@
+// DR 314 - template in base class specifier.
+
+template <typename T>
+struct A {
+  template <typename U>
+  struct B {};
+};
+
+template <typename T>
+struct C : public A<T>::template B<T> {
+};

base-commit: 3d42842c07f4143042f3dcc39a050b262bcf1b55
Li, Pan2 via Gcc-patches March 20, 2020, 6:12 p.m. UTC | #3
On 3/20/20 1:06 PM, Marek Polacek wrote:
> On Thu, Mar 19, 2020 at 05:40:01PM -0400, Jason Merrill wrote:
>> On 3/19/20 5:28 PM, Marek Polacek wrote:
>>> Consider
>>>
>>>     template <typename T> class A {
>>>       template <typename U> class B {
>>>         void fn(typename A<T>::B<U>);
>>>       };
>>>     };
>>>
>>> which is rejected with
>>> error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
>>> whereas clang/icc/msvc accept it.
>>>
>>> "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
>>> don't mention it anywhere, because the typename-specifier wasn't in C++11;
>>> it was only added to the language in N1376.  Instead, we handle it as
>>> an elaborated-type-specifier (not a problem thus far).   So we get to
>>> cp_parser_nested_name_specifier_opt which has a loop that breaks if we
>>> don't see a < or ::, but that means we can -- tentatively -- parse even
>>> B<U> which is not a nested-name-specifier (it doesn't end with a ::).
>>>
>>> Even though we're parsing B<U> tentatively, we issue an error in
>>> cp_parser_class_name -> make_typename_type, but here we should not.  In
>>> fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
>>> cp_parser_class_name hunk.
>>>
>>> I think this should compile because [temp.names]/4 says: "In a qualified-id
>>> used as the name in a typename-specifier, elaborated-type-specifier,
>>> using-declaration, or class-or-decltype, an optional keyword template
>>> appearing at the top level is ignored.", added in DR 1710.  Also see
>>> DR 1812.
>>
>> Looks good, but please add tests for the other contexts mentioned in that
>> passage.
> 
> Wonderful.  I've added a bunch of tests, and some from the related DRs too.
> But I had a problem with the class-or-decltype case: if we have
> 
> template<typename T> struct D : T::template B<int>::template C<int> {};
> 
> then we still require all the 'template' keywords here (as does clang).  So I'm
> kind of confused, but I don't think it's a big deal right now.

This seems related enough that I'd like to fix it at the same time; why 
doesn't your patch fix it?  Is it because typename_p is false?

> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> -- >8 --
> Consider
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(typename A<T>::B<U>);
>      };
>    };
> 
> which is rejected with
> error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
> whereas clang/icc/msvc accept it.
> 
> "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
> don't mention it anywhere, because the typename-specifier wasn't in C++11;
> it was only added to the language in N1376.  Instead, we handle it as
> an elaborated-type-specifier (not a problem thus far).   So we get to
> cp_parser_nested_name_specifier_opt which has a loop that breaks if we
> don't see a < or ::, but that means we can -- tentatively -- parse even
> B<U> which is not a nested-name-specifier (it doesn't end with a ::).
> 
> Even though we're parsing B<U> tentatively, we issue an error in
> cp_parser_class_name -> make_typename_type, but here we should not.  In
> fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
> cp_parser_class_name hunk.
> 
> I think this should compile because [temp.names]/4 says: "In a qualified-id
> used as the name in a typename-specifier, elaborated-type-specifier,
> using-declaration, or class-or-decltype, an optional keyword template
> appearing at the top level is ignored.", added in DR 1710.  Also see
> DR 1812.
> 
> This issue on its own is not a significant problem or a regression.
> However, in C++20, the typename here becomes optional, and so this test
> is rejected in C++20, but accepted in C++17:
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(A<T>::B<U>);
>      };
>    };
> 
> Here we morph A<T>::B<U> into a typename-specifier, but that happens
> in cp_parser_simple_type_specifier and we never handle it as above.
> To fake the template keyword I'm afraid we need to use cp_parser_template_id
> with template_keyword_p=true as in the patch below.  The tricky thing
> is to avoid breaking concepts.
> 
> 	DR 1710
> 	PR c++/94057 - template keyword in a typename-specifier.
> 	* parser.c (cp_parser_simple_type_specifier): Assume that a <
> 	following a qualified-id in a typename-specifier begins
> 	a template argument list.
> 	(cp_parser_class_name): Complain only if not parsing tentatively.
> 
> 	* g++.dg/template/dependent-name5.C: Update dg-error.
> 	* g++.dg/template/dependent-name7.C: New test.
> 	* g++.dg/template/dependent-name8.C: New test.
> 	* g++.dg/template/dependent-name9.C: New test.
> 	* g++.dg/template/dependent-name10.C: New test.
> 	* g++.dg/template/dependent-name11.C: New test.
> 	* g++.dg/template/dr1794.C: New test.
> 	* g++.dg/template/dr314.C: New test.
> 	* g++.dg/template/dr1710.C: New test.

Please also add the parallel tests omitting the template keyword.

> ---
>   gcc/cp/parser.c                               | 32 +++++++++++++++++--
>   .../g++.dg/template/dependent-name10.C        | 18 +++++++++++
>   .../g++.dg/template/dependent-name11.C        | 15 +++++++++
>   .../g++.dg/template/dependent-name5.C         |  2 --
>   .../g++.dg/template/dependent-name7.C         |  9 ++++++
>   .../g++.dg/template/dependent-name8.C         |  9 ++++++
>   .../g++.dg/template/dependent-name9.C         |  9 ++++++
>   gcc/testsuite/g++.dg/template/dr1710.C        |  4 +++
>   gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++++++
>   gcc/testsuite/g++.dg/template/dr314.C         | 11 +++++++
>   10 files changed, 119 insertions(+), 4 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr314.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index cbd5510a8fb..f4175955992 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -18113,6 +18113,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>   		}
>   	    }
>   	}
> +      /* DR 1812: A < following a qualified-id in a typename-specifier
> +	 could safely be assumed to begin a template argument list, so
> +	 the template keyword should be optional.  */
> +      else if (parser->scope
> +	       && qualified_p
> +	       && typename_p
> +	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
> +	{
> +	  cp_parser_parse_tentatively (parser);
> +
> +	  type = cp_parser_template_id (parser,
> +					/*template_keyword_p=*/true,
> +					/*check_dependency_p=*/true,
> +					none_type,
> +					/*is_declaration=*/false);
> +	  /* This is handled below, so back off.  */
> +	  if (type && concept_check_p (type))
> +	    cp_parser_simulate_error (parser);
> +
> +	  if (!cp_parser_parse_definitely (parser))
> +	    type = NULL_TREE;
> +	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
> +	    type = make_typename_type (parser->scope, type, typename_type,
> +				       /*complain=*/tf_error);
> +	  else if (TREE_CODE (type) != TYPE_DECL)
> +	    type = NULL_TREE;
> +	}
>   
>         /* Otherwise, look for a type-name.  */
>         if (!type)
> @@ -23636,8 +23663,9 @@ cp_parser_class_name (cp_parser *parser,
>         && decl != error_mark_node
>         && !is_overloaded_fn (decl))
>       {
> -      decl = make_typename_type (scope, decl, typename_type,
> -				 /*complain=*/tf_error);
> +      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
> +				 ? tf_none : tf_error);
> +      decl = make_typename_type (scope, decl, typename_type, complain);
>         if (decl != error_mark_node)
>   	decl = TYPE_NAME (decl);
>       }
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
> new file mode 100644
> index 00000000000..18e024f7e6d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
> @@ -0,0 +1,18 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    typedef int TT;
> +    typedef int TT2;
> +    typedef int TT3;
> +    typedef int TT4;
> +  };
> +};
> +
> +struct X : A<int>::B<int> {
> +  using A<int>::template B<int>::TT;
> +  using typename A<int>::template B<int>::TT2;
> +  using A<int>::B<int>::TT3;
> +  using typename A<int>::B<int>::TT4;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
> new file mode 100644
> index 00000000000..687a9bd5df5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
> @@ -0,0 +1,15 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U>
> +  struct W { };
> +};
> +
> +void
> +g ()
> +{
> +  // class-key nested-name-specifier template[opt] simple-template-id
> +  struct A<int>::W<int> w;
> +  struct A<int>::template W<int> w2;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
> index fc78983324b..15c1acb0347 100644
> --- a/gcc/testsuite/g++.dg/template/dependent-name5.C
> +++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
> @@ -22,9 +22,7 @@ struct A
>   
>     typedef N<int>       type6;
>     typedef A::N<int>    type7;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T>::N<int> type8;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
>     typedef typename A<T*>::template N<int> type10;
>   
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
> new file mode 100644
> index 00000000000..3dfa42d2df0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(A<T>::B<U>&);
> +    void fn(A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
> new file mode 100644
> index 00000000000..ad9e44f9b85
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::B<U>&);
> +    void fn(typename A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
> new file mode 100644
> index 00000000000..6dfdbc176c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::template B<U>&);
> +    void fn(typename A<T>::template B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
> new file mode 100644
> index 00000000000..fbbccde9bbf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710.C
> @@ -0,0 +1,4 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct D : T::template B<int>::template C<int> {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
> new file mode 100644
> index 00000000000..f629d7d0b98
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1794.C
> @@ -0,0 +1,14 @@
> +// DR 1794 - template keyword and alias templates.
> +// { dg-do compile { target c++11 } }
> +
> +template<template<typename> class Template>
> +struct Internal {
> +  template<typename Arg>
> +  using Bind = Template<Arg>;
> +};
> +
> +template<template<typename> class Template, typename Arg>
> +using Instantiate = Template<Arg>;
> +
> +template<template<typename> class Template, typename Argument>
> +using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
> diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
> new file mode 100644
> index 00000000000..e3c9c9e5a0e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr314.C
> @@ -0,0 +1,11 @@
> +// DR 314 - template in base class specifier.
> +
> +template <typename T>
> +struct A {
> +  template <typename U>
> +  struct B {};
> +};
> +
> +template <typename T>
> +struct C : public A<T>::template B<T> {
> +};
> 
> base-commit: 3d42842c07f4143042f3dcc39a050b262bcf1b55
>
Li, Pan2 via Gcc-patches March 20, 2020, 11:02 p.m. UTC | #4
On Fri, Mar 20, 2020 at 02:12:49PM -0400, Jason Merrill wrote:
> On 3/20/20 1:06 PM, Marek Polacek wrote:
> > Wonderful.  I've added a bunch of tests, and some from the related DRs too.
> > But I had a problem with the class-or-decltype case: if we have
> > 
> > template<typename T> struct D : T::template B<int>::template C<int> {};
> > 
> > then we still require all the 'template' keywords here (as does clang).  So I'm
> > kind of confused, but I don't think it's a big deal right now.
> 
> This seems related enough that I'd like to fix it at the same time; why
> doesn't your patch fix it?  Is it because typename_p is false?

Ah, I was mistaken.  Of course we need the template keyword here: it's a member of an
unknown specialization!  So I think there's no problem, and I've...

> > 	* g++.dg/template/dependent-name5.C: Update dg-error.
> > 	* g++.dg/template/dependent-name7.C: New test.
> > 	* g++.dg/template/dependent-name8.C: New test.
> > 	* g++.dg/template/dependent-name9.C: New test.
> > 	* g++.dg/template/dependent-name10.C: New test.
> > 	* g++.dg/template/dependent-name11.C: New test.
> > 	* g++.dg/template/dr1794.C: New test.
> > 	* g++.dg/template/dr314.C: New test.
> > 	* g++.dg/template/dr1710.C: New test.
> 
> Please also add the parallel tests omitting the template keyword.

...added some tests to that effect (added dr1710-2.C, expanded dr314.C).
Sorry about the confusion.

Is is OK now?  Thanks,

-- >8 --
Consider

  template <typename T> class A {
    template <typename U> class B {
      void fn(typename A<T>::B<U>);
    };
  };

which is rejected with
error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
whereas clang/icc/msvc accept it.

"typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
don't mention it anywhere, because the typename-specifier wasn't in C++11;
it was only added to the language in N1376.  Instead, we handle it as
an elaborated-type-specifier (not a problem thus far).   So we get to
cp_parser_nested_name_specifier_opt which has a loop that breaks if we
don't see a < or ::, but that means we can -- tentatively -- parse even
B<U> which is not a nested-name-specifier (it doesn't end with a ::).

Even though we're parsing B<U> tentatively, we issue an error in
cp_parser_class_name -> make_typename_type, but here we should not.  In
fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
cp_parser_class_name hunk.

I think this should compile because [temp.names]/4 says: "In a qualified-id
used as the name in a typename-specifier, elaborated-type-specifier,
using-declaration, or class-or-decltype, an optional keyword template
appearing at the top level is ignored.", added in DR 1710.  Also see
DR 1812.

This issue on its own is not a significant problem or a regression.
However, in C++20, the typename here becomes optional, and so this test
is rejected in C++20, but accepted in C++17:

  template <typename T> class A {
    template <typename U> class B {
      void fn(A<T>::B<U>);
    };
  };

Here we morph A<T>::B<U> into a typename-specifier, but that happens
in cp_parser_simple_type_specifier and we never handle it as above.
To fake the template keyword I'm afraid we need to use cp_parser_template_id
with template_keyword_p=true as in the patch below.  The tricky thing
is to avoid breaking concepts.

	DR 1710
	PR c++/94057 - template keyword in a typename-specifier.
	* parser.c (cp_parser_simple_type_specifier): Assume that a <
	following a qualified-id in a typename-specifier begins
	a template argument list.
	(cp_parser_class_name): Complain only if not parsing tentatively.

	* g++.dg/template/dependent-name5.C: Update dg-error.
	* g++.dg/template/dependent-name7.C: New test.
	* g++.dg/template/dependent-name8.C: New test.
	* g++.dg/template/dependent-name9.C: New test.
	* g++.dg/template/dependent-name10.C: New test.
	* g++.dg/template/dependent-name11.C: New test.
	* g++.dg/template/dr1794.C: New test.
	* g++.dg/template/dr314.C: New test.
	* g++.dg/template/dr1710.C: New test.
	* g++.dg/template/dr1710-2.C: New test.
---
 gcc/cp/parser.c                               | 32 +++++++++++++++++--
 .../g++.dg/template/dependent-name10.C        | 18 +++++++++++
 .../g++.dg/template/dependent-name11.C        | 15 +++++++++
 .../g++.dg/template/dependent-name5.C         |  2 --
 .../g++.dg/template/dependent-name7.C         |  9 ++++++
 .../g++.dg/template/dependent-name8.C         |  9 ++++++
 .../g++.dg/template/dependent-name9.C         |  9 ++++++
 gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 ++++++
 gcc/testsuite/g++.dg/template/dr1710.C        |  4 +++
 gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++++++
 gcc/testsuite/g++.dg/template/dr314.C         | 15 +++++++++
 11 files changed, 133 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr314.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..f4175955992 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18113,6 +18113,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 		}
 	    }
 	}
+      /* DR 1812: A < following a qualified-id in a typename-specifier
+	 could safely be assumed to begin a template argument list, so
+	 the template keyword should be optional.  */
+      else if (parser->scope
+	       && qualified_p
+	       && typename_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
+	{
+	  cp_parser_parse_tentatively (parser);
+
+	  type = cp_parser_template_id (parser,
+					/*template_keyword_p=*/true,
+					/*check_dependency_p=*/true,
+					none_type,
+					/*is_declaration=*/false);
+	  /* This is handled below, so back off.  */
+	  if (type && concept_check_p (type))
+	    cp_parser_simulate_error (parser);
+
+	  if (!cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
+	    type = make_typename_type (parser->scope, type, typename_type,
+				       /*complain=*/tf_error);
+	  else if (TREE_CODE (type) != TYPE_DECL)
+	    type = NULL_TREE;
+	}
 
       /* Otherwise, look for a type-name.  */
       if (!type)
@@ -23636,8 +23663,9 @@ cp_parser_class_name (cp_parser *parser,
       && decl != error_mark_node
       && !is_overloaded_fn (decl))
     {
-      decl = make_typename_type (scope, decl, typename_type,
-				 /*complain=*/tf_error);
+      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
+				 ? tf_none : tf_error);
+      decl = make_typename_type (scope, decl, typename_type, complain);
       if (decl != error_mark_node)
 	decl = TYPE_NAME (decl);
     }
diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
new file mode 100644
index 00000000000..18e024f7e6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
@@ -0,0 +1,18 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    typedef int TT;
+    typedef int TT2;
+    typedef int TT3;
+    typedef int TT4;
+  };
+};
+
+struct X : A<int>::B<int> {
+  using A<int>::template B<int>::TT;
+  using typename A<int>::template B<int>::TT2;
+  using A<int>::B<int>::TT3;
+  using typename A<int>::B<int>::TT4;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
new file mode 100644
index 00000000000..687a9bd5df5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
@@ -0,0 +1,15 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U>
+  struct W { };
+};
+
+void
+g ()
+{
+  // class-key nested-name-specifier template[opt] simple-template-id
+  struct A<int>::W<int> w;
+  struct A<int>::template W<int> w2;
+}
diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
index fc78983324b..15c1acb0347 100644
--- a/gcc/testsuite/g++.dg/template/dependent-name5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -22,9 +22,7 @@ struct A
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
new file mode 100644
index 00000000000..3dfa42d2df0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(A<T>::B<U>&);
+    void fn(A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
new file mode 100644
index 00000000000..ad9e44f9b85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::B<U>&);
+    void fn(typename A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
new file mode 100644
index 00000000000..6dfdbc176c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::template B<U>&);
+    void fn(typename A<T>::template B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
new file mode 100644
index 00000000000..99d49b746b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
@@ -0,0 +1,10 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+  };
+};
+
+template<typename T> struct D : A<int>::B<int> {};
+template<typename T> struct D2 : A<int>::template B<int> {};
diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
new file mode 100644
index 00000000000..fbbccde9bbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710.C
@@ -0,0 +1,4 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct D : T::template B<int>::template C<int> {};
diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
new file mode 100644
index 00000000000..f629d7d0b98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1794.C
@@ -0,0 +1,14 @@
+// DR 1794 - template keyword and alias templates.
+// { dg-do compile { target c++11 } }
+
+template<template<typename> class Template>
+struct Internal {
+  template<typename Arg>
+  using Bind = Template<Arg>;
+};
+
+template<template<typename> class Template, typename Arg>
+using Instantiate = Template<Arg>;
+
+template<template<typename> class Template, typename Argument>
+using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
new file mode 100644
index 00000000000..7c0d8ac592f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr314.C
@@ -0,0 +1,15 @@
+// DR 314 - template in base class specifier.
+
+template <typename T>
+struct A {
+  template <typename U>
+  struct B {};
+};
+
+template <typename T>
+struct C : public A<T>::template B<T> {
+};
+
+template <typename T>
+struct C2 : public A<int>::B<T> {
+};

base-commit: 72b3bc895f023bf451357659cfe96c966945bdf9
Li, Pan2 via Gcc-patches March 23, 2020, 2:41 p.m. UTC | #5
On 3/20/20 7:02 PM, Marek Polacek wrote:
> On Fri, Mar 20, 2020 at 02:12:49PM -0400, Jason Merrill wrote:
>> On 3/20/20 1:06 PM, Marek Polacek wrote:
>>> Wonderful.  I've added a bunch of tests, and some from the related DRs too.
>>> But I had a problem with the class-or-decltype case: if we have
>>>
>>> template<typename T> struct D : T::template B<int>::template C<int> {};
>>>
>>> then we still require all the 'template' keywords here (as does clang).  So I'm
>>> kind of confused, but I don't think it's a big deal right now.
>>
>> This seems related enough that I'd like to fix it at the same time; why
>> doesn't your patch fix it?  Is it because typename_p is false?
> 
> Ah, I was mistaken.  Of course we need the template keyword here: it's a member of an
> unknown specialization!

That's why it's needed in contexts where we don't know whether or not 
we're naming a type.  But where we do know that, as in a base-specifier, 
it isn't necessary.  This is exactly DR 1710: "The keyword template is 
optional in a ... class-or-decltype (Clause 11.7 [class.derived])...."

Jason
Li, Pan2 via Gcc-patches March 24, 2020, 3:45 p.m. UTC | #6
On Mon, Mar 23, 2020 at 10:41:28AM -0400, Jason Merrill wrote:
> On 3/20/20 7:02 PM, Marek Polacek wrote:
> > On Fri, Mar 20, 2020 at 02:12:49PM -0400, Jason Merrill wrote:
> > > On 3/20/20 1:06 PM, Marek Polacek wrote:
> > > > Wonderful.  I've added a bunch of tests, and some from the related DRs too.
> > > > But I had a problem with the class-or-decltype case: if we have
> > > > 
> > > > template<typename T> struct D : T::template B<int>::template C<int> {};
> > > > 
> > > > then we still require all the 'template' keywords here (as does clang).  So I'm
> > > > kind of confused, but I don't think it's a big deal right now.
> > > 
> > > This seems related enough that I'd like to fix it at the same time; why
> > > doesn't your patch fix it?  Is it because typename_p is false?
> > 
> > Ah, I was mistaken.  Of course we need the template keyword here: it's a member of an
> > unknown specialization!
> 
> That's why it's needed in contexts where we don't know whether or not we're
> naming a type.  But where we do know that, as in a base-specifier, it isn't
> necessary.  This is exactly DR 1710: "The keyword template is optional in a
> ... class-or-decltype (Clause 11.7 [class.derived])...."

Duh, not sure what I was thinking.

I've implemented that in cp_parser_nested_name_specifier_opt: assume 'template'
if we've seen 'typename'.  If it turns out that this is wrong because it
triggers in contexts where it shouldn't, we'll have to introduce something like
CP_PARSER_FLAGS_TEMPLATE_OPTIONAL.

With this another problem revealed: we weren't accepting an alias template
after 'template' which DR1710 says is valid.  Fixed by the TYPE_ALIAS_P hunk
in the new check_template_keyword_in_nested_name_spec. (alias-decl1.C)

But this is still not over: I noticed that 

template <typename T> struct S {
  template <typename TT>
  using U = TT;
};
template <typename T> typename S<int>::template U<T>::type foo;

is still rejected and shouldn't be.  Here check_template_keyword_in_nested_name_spec
gets 

 <template_type_parm 0x7fffeaa42690 U type_0 VOID
    align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fffeaa420a8
   index 0 level 1 orig_level 2
    chain <type_decl 0x7fffea90bda8 TT>>

Any suggestions how to handle this?

FWIW, bootstrapped/regtested on x86_64-linux.

-- >8 --
Consider

  template <typename T> class A {
    template <typename U> class B {
      void fn(typename A<T>::B<U>);
    };
  };

which is rejected with
error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
whereas clang/icc/msvc accept it.

"typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
don't mention it anywhere, because the typename-specifier wasn't in C++11;
it was only added to the language in N1376.  Instead, we handle it as
an elaborated-type-specifier (not a problem thus far).   So we get to
cp_parser_nested_name_specifier_opt which has a loop that breaks if we
don't see a < or ::, but that means we can -- tentatively -- parse even
B<U> which is not a nested-name-specifier (it doesn't end with a ::).

Even though we're parsing B<U> tentatively, we issue an error in
cp_parser_class_name -> make_typename_type, but here we should not.  In
fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
cp_parser_class_name hunk.

I think this should compile because [temp.names]/4 says: "In a qualified-id
used as the name in a typename-specifier, elaborated-type-specifier,
using-declaration, or class-or-decltype, an optional keyword template
appearing at the top level is ignored.", added in DR 1710.  Also see
DR 1812.

This issue on its own is not a significant problem or a regression.
However, in C++20, the typename here becomes optional, and so this test
is rejected in C++20, but accepted in C++17:

  template <typename T> class A {
    template <typename U> class B {
      void fn(A<T>::B<U>);
    };
  };

Here we morph A<T>::B<U> into a typename-specifier, but that happens
in cp_parser_simple_type_specifier and we never handle it as above.
To fake the template keyword I'm afraid we need to use cp_parser_template_id
with template_keyword_p=true as in the patch below.  The tricky thing
is to avoid breaking concepts.

To handle DR 1710, I made cp_parser_nested_name_specifier_opt assume that
when we're naming a type, the template keyword is present, too.  That
revealed a bug: DR 1710 also says that the template keyword can be followed
by an alias template, but we weren't prepared to handle that.  alias-decl1.C
exercise this.

	DR 1710
	PR c++/94057 - template keyword in a typename-specifier.
	* parser.c (check_template_keyword_in_nested_name_spec): New.
	(cp_parser_nested_name_specifier_opt): Implement DR1710, optional
	'template'.  Call check_template_keyword_in_nested_name_spec.
	(cp_parser_simple_type_specifier): Assume that a <
	following a qualified-id in a typename-specifier begins
	a template argument list.
	(cp_parser_class_name): Complain only if not parsing tentatively.

	* g++.dg/cpp1y/alias-decl1.C: New test.
	* g++.dg/parse/missing-template1.C: Update dg-error.
	* g++.dg/parse/template3.C: Likewise.
	* g++.dg/template/error4.C: Likewise.
	* g++.dg/template/meminit2.C: Likewise.
	* g++.dg/template/dependent-name5.C: Likewise.
	* g++.dg/template/dependent-name7.C: New test.
	* g++.dg/template/dependent-name8.C: New test.
	* g++.dg/template/dependent-name9.C: New test.
	* g++.dg/template/dependent-name10.C: New test.
	* g++.dg/template/dependent-name11.C: New test.
	* g++.dg/template/dependent-name12.C: New test.
	* g++.dg/template/dependent-name13.C: New test.
	* g++.dg/template/dr1794.C: New test.
	* g++.dg/template/dr314.C: New test.
	* g++.dg/template/dr1710.C: New test.
	* g++.dg/template/dr1710-2.C: New test.
	* g++.old-deja/g++.pt/crash38.C: Update dg-error.
---
 gcc/cp/parser.c                               | 91 ++++++++++++++++---
 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C      |  9 ++
 .../g++.dg/parse/missing-template1.C          |  4 +-
 gcc/testsuite/g++.dg/parse/template3.C        |  5 +-
 .../g++.dg/template/dependent-name10.C        | 18 ++++
 .../g++.dg/template/dependent-name11.C        | 15 +++
 .../g++.dg/template/dependent-name12.C        |  7 ++
 .../g++.dg/template/dependent-name13.C        |  8 ++
 .../g++.dg/template/dependent-name5.C         |  2 -
 .../g++.dg/template/dependent-name7.C         |  9 ++
 .../g++.dg/template/dependent-name8.C         |  9 ++
 .../g++.dg/template/dependent-name9.C         |  9 ++
 gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 ++
 gcc/testsuite/g++.dg/template/dr1710.C        |  9 ++
 gcc/testsuite/g++.dg/template/dr1794.C        | 14 +++
 gcc/testsuite/g++.dg/template/dr314.C         | 15 +++
 gcc/testsuite/g++.dg/template/error4.C        |  3 +-
 gcc/testsuite/g++.dg/template/meminit2.C      |  4 +-
 gcc/testsuite/g++.old-deja/g++.pt/crash38.C   |  6 +-
 19 files changed, 214 insertions(+), 33 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name12.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name13.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr314.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..824fb18e83a 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -6266,6 +6266,39 @@ cp_parser_unqualified_id (cp_parser* parser,
     }
 }
 
+/* Check [temp.names]/5: A name prefixed by the keyword template shall
+   be a template-id or the name shall refer to a class template or an
+   alias template.  */
+
+static void
+check_template_keyword_in_nested_name_spec (tree name)
+{
+  if (CLASS_TYPE_P (name)
+      && ((CLASSTYPE_USE_TEMPLATE (name)
+	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
+	  || CLASSTYPE_IS_TEMPLATE (name)))
+    return;
+
+
+  if (TREE_CODE (name) == TYPENAME_TYPE)
+    {
+      if (TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
+	return;
+      /* Alias templates are also OK.  */
+      else if (TYPE_ALIAS_P (name))
+	{
+	  tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (name);
+	  if (tinfo && DECL_ALIAS_TEMPLATE_P (TI_TEMPLATE (tinfo)))
+	    return;
+	}
+    }
+
+  permerror (input_location, TYPE_P (name)
+	     ? G_("%qT is not a template")
+	     : G_("%qD is not a template"),
+	     name);
+}
+
 /* Parse an (optional) nested-name-specifier.
 
    nested-name-specifier: [C++98]
@@ -6389,7 +6422,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
       /* Look for the optional `template' keyword, if this isn't the
 	 first time through the loop.  */
       if (success)
-	template_keyword_p = cp_parser_optional_template_keyword (parser);
+	{
+	  template_keyword_p = cp_parser_optional_template_keyword (parser);
+	  /* DR1710: "In a qualified-id used as the name in
+	     a typename-specifier, elaborated-type-specifier, using-declaration,
+	     or class-or-decltype, an optional keyword template appearing at
+	     the top level is ignored."  */
+	  if (!template_keyword_p
+	      && typename_keyword_p
+	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
+	    template_keyword_p = true;
+	}
 
       /* Save the old scope since the name lookup we are about to do
 	 might destroy it.  */
@@ -6580,18 +6623,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
       if (TREE_CODE (new_scope) == TYPE_DECL)
 	new_scope = TREE_TYPE (new_scope);
       /* Uses of "template" must be followed by actual templates.  */
-      if (template_keyword_p
-	  && !(CLASS_TYPE_P (new_scope)
-	       && ((CLASSTYPE_USE_TEMPLATE (new_scope)
-		    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope)))
-		   || CLASSTYPE_IS_TEMPLATE (new_scope)))
-	  && !(TREE_CODE (new_scope) == TYPENAME_TYPE
-	       && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope))
-		   == TEMPLATE_ID_EXPR)))
-	permerror (input_location, TYPE_P (new_scope)
-		   ? G_("%qT is not a template")
-		   : G_("%qD is not a template"),
-		   new_scope);
+      if (template_keyword_p)
+	check_template_keyword_in_nested_name_spec (new_scope);
       /* If it is a class scope, try to complete it; we are about to
 	 be looking up names inside the class.  */
       if (TYPE_P (new_scope)
@@ -18113,6 +18146,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 		}
 	    }
 	}
+      /* DR 1812: A < following a qualified-id in a typename-specifier
+	 could safely be assumed to begin a template argument list, so
+	 the template keyword should be optional.  */
+      else if (parser->scope
+	       && qualified_p
+	       && typename_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
+	{
+	  cp_parser_parse_tentatively (parser);
+
+	  type = cp_parser_template_id (parser,
+					/*template_keyword_p=*/true,
+					/*check_dependency_p=*/true,
+					none_type,
+					/*is_declaration=*/false);
+	  /* This is handled below, so back off.  */
+	  if (type && concept_check_p (type))
+	    cp_parser_simulate_error (parser);
+
+	  if (!cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
+	    type = make_typename_type (parser->scope, type, typename_type,
+				       /*complain=*/tf_error);
+	  else if (TREE_CODE (type) != TYPE_DECL)
+	    type = NULL_TREE;
+	}
 
       /* Otherwise, look for a type-name.  */
       if (!type)
@@ -23636,8 +23696,9 @@ cp_parser_class_name (cp_parser *parser,
       && decl != error_mark_node
       && !is_overloaded_fn (decl))
     {
-      decl = make_typename_type (scope, decl, typename_type,
-				 /*complain=*/tf_error);
+      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
+				 ? tf_none : tf_error);
+      decl = make_typename_type (scope, decl, typename_type, complain);
       if (decl != error_mark_node)
 	decl = TYPE_NAME (decl);
     }
diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
new file mode 100644
index 00000000000..e5397e71bd6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile { target c++14 } }
+
+template <int> struct S {
+  template <int> struct A;
+  template <int N> using U = typename A<N>::foo;
+};
+template <typename T> typename S<1>::U<T::foo>::type a;
+template <typename T> typename S<1>::template U<T::foo>::type a2;
diff --git a/gcc/testsuite/g++.dg/parse/missing-template1.C b/gcc/testsuite/g++.dg/parse/missing-template1.C
index fcc3aa0c3ff..19c1433d09f 100644
--- a/gcc/testsuite/g++.dg/parse/missing-template1.C
+++ b/gcc/testsuite/g++.dg/parse/missing-template1.C
@@ -12,9 +12,7 @@ template <typename T> struct A
 
 template <typename T> void foo()
 {
-    typedef typename A<T>::B<T>::X Y; // { dg-error "non-template" "non" }
-    // { dg-error "not declare" "decl" { target *-*-* } .-1 }
-    // { dg-message "note" "note" { target *-*-* } .-2 }
+    typedef typename A<T>::B<T>::X Y;
 }
 
 void bar()
diff --git a/gcc/testsuite/g++.dg/parse/template3.C b/gcc/testsuite/g++.dg/parse/template3.C
index c284a5ee040..8da8a48a3c3 100644
--- a/gcc/testsuite/g++.dg/parse/template3.C
+++ b/gcc/testsuite/g++.dg/parse/template3.C
@@ -13,7 +13,4 @@ struct X : Outer<b>::template Inner<T>
 {};
 
 template <bool b, typename T>
-struct Y : Outer<b>::Inner<T> {}; // { dg-error "used as template" "temp" }
-// { dg-error "expected" "exp" { target *-*-* } .-1 }
-// { dg-message "note" "note" { target *-*-* } .-2 }
-
+struct Y : Outer<b>::Inner<T> {};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
new file mode 100644
index 00000000000..18e024f7e6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
@@ -0,0 +1,18 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    typedef int TT;
+    typedef int TT2;
+    typedef int TT3;
+    typedef int TT4;
+  };
+};
+
+struct X : A<int>::B<int> {
+  using A<int>::template B<int>::TT;
+  using typename A<int>::template B<int>::TT2;
+  using A<int>::B<int>::TT3;
+  using typename A<int>::B<int>::TT4;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
new file mode 100644
index 00000000000..687a9bd5df5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
@@ -0,0 +1,15 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U>
+  struct W { };
+};
+
+void
+g ()
+{
+  // class-key nested-name-specifier template[opt] simple-template-id
+  struct A<int>::W<int> w;
+  struct A<int>::template W<int> w2;
+}
diff --git a/gcc/testsuite/g++.dg/template/dependent-name12.C b/gcc/testsuite/g++.dg/template/dependent-name12.C
new file mode 100644
index 00000000000..7ee94e7457d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name12.C
@@ -0,0 +1,7 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+
+template <bool> struct A;
+template <typename, typename> struct B;
+template <typename T, typename U, typename V> struct B<T U::*, V> {
+  typename A<V::x>::type::type t;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name13.C b/gcc/testsuite/g++.dg/template/dependent-name13.C
new file mode 100644
index 00000000000..1e971168657
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name13.C
@@ -0,0 +1,8 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+
+template<typename T> struct S {
+  void fn(typename T::template B<int>::template C<int>);
+  void fn2(typename T::B<int>::template C<int>);
+  void fn3(typename T::template B<int>::C<int>);
+  void fn4(typename T::B<int>::C<int>);
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
index fc78983324b..15c1acb0347 100644
--- a/gcc/testsuite/g++.dg/template/dependent-name5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -22,9 +22,7 @@ struct A
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
new file mode 100644
index 00000000000..3dfa42d2df0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(A<T>::B<U>&);
+    void fn(A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
new file mode 100644
index 00000000000..ad9e44f9b85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::B<U>&);
+    void fn(typename A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
new file mode 100644
index 00000000000..6dfdbc176c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::template B<U>&);
+    void fn(typename A<T>::template B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
new file mode 100644
index 00000000000..99d49b746b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
@@ -0,0 +1,10 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+  };
+};
+
+template<typename T> struct D : A<int>::B<int> {};
+template<typename T> struct D2 : A<int>::template B<int> {};
diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
new file mode 100644
index 00000000000..c945977971f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct D : T::template B<int>::template C<int> {};
+template<typename T> struct D2 : T::B<int>::template C<int> {};
+template<typename T> struct D3 : T::template B<int>::C<int> {};
+template<typename T> struct D4 : T::B<int>::C<int> {};
+template<typename T> struct D5 : T::template B<int>::type::type {};
+template<typename T> struct D6 : T::B<int>::type::type {};
diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
new file mode 100644
index 00000000000..f629d7d0b98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1794.C
@@ -0,0 +1,14 @@
+// DR 1794 - template keyword and alias templates.
+// { dg-do compile { target c++11 } }
+
+template<template<typename> class Template>
+struct Internal {
+  template<typename Arg>
+  using Bind = Template<Arg>;
+};
+
+template<template<typename> class Template, typename Arg>
+using Instantiate = Template<Arg>;
+
+template<template<typename> class Template, typename Argument>
+using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
new file mode 100644
index 00000000000..7c0d8ac592f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr314.C
@@ -0,0 +1,15 @@
+// DR 314 - template in base class specifier.
+
+template <typename T>
+struct A {
+  template <typename U>
+  struct B {};
+};
+
+template <typename T>
+struct C : public A<T>::template B<T> {
+};
+
+template <typename T>
+struct C2 : public A<int>::B<T> {
+};
diff --git a/gcc/testsuite/g++.dg/template/error4.C b/gcc/testsuite/g++.dg/template/error4.C
index 9d76561aa02..a5030f06c98 100644
--- a/gcc/testsuite/g++.dg/template/error4.C
+++ b/gcc/testsuite/g++.dg/template/error4.C
@@ -5,5 +5,4 @@ template<class T> struct C1
 };
 
 template<class T, class U>
-void foo(typename C1<T>::C2<U>::Type *) { } // { dg-error "template" "error " }
-// { dg-message "note" "note" { target *-*-* } .-1 }
+void foo(typename C1<T>::C2<U>::Type *) { }
diff --git a/gcc/testsuite/g++.dg/template/meminit2.C b/gcc/testsuite/g++.dg/template/meminit2.C
index db6e0427fc7..6a56a806746 100644
--- a/gcc/testsuite/g++.dg/template/meminit2.C
+++ b/gcc/testsuite/g++.dg/template/meminit2.C
@@ -14,8 +14,6 @@ struct A : typename O<T>::template I<int> {   // { dg-error "keyword 'typename'
 
 template <typename T> 
 struct B : O<T>::template I<int> {
-  B() :    O<T>::I<int>()   // { dg-error "used as template|it is a template" }
+  B() :    O<T>::I<int>()
   {}
 };
-
-// { dg-bogus "end of input" "bogus token skipping in the parser" { xfail *-*-* } 17 }
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
index 5d386132436..60cd5e3f44e 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
@@ -3,10 +3,8 @@
 
 template <class T>
 struct S {
-  typedef typename T::Y<T>::Z X; // { dg-error "non-template" "non-template" } No Y in A
-// { dg-message "note" "note" { target *-*-* } .-1 }
-// { dg-error "does not declare" "not declare" { target *-*-* } .-2 }
-  X x; // { dg-error "does not name a type" } No Y in A
+  typedef typename T::Y<T>::Z X; // { dg-error "not a class template" } No Y in A
+  X x;
 };
 
 struct A {

base-commit: c2211a60ff05b7a0289d3e287e72c181bb4d5d8b
Li, Pan2 via Gcc-patches March 25, 2020, 4 a.m. UTC | #7
On 3/24/20 11:45 AM, Marek Polacek wrote:
> On Mon, Mar 23, 2020 at 10:41:28AM -0400, Jason Merrill wrote:
>> On 3/20/20 7:02 PM, Marek Polacek wrote:
>>> On Fri, Mar 20, 2020 at 02:12:49PM -0400, Jason Merrill wrote:
>>>> On 3/20/20 1:06 PM, Marek Polacek wrote:
>>>>> Wonderful.  I've added a bunch of tests, and some from the related DRs too.
>>>>> But I had a problem with the class-or-decltype case: if we have
>>>>>
>>>>> template<typename T> struct D : T::template B<int>::template C<int> {};
>>>>>
>>>>> then we still require all the 'template' keywords here (as does clang).  So I'm
>>>>> kind of confused, but I don't think it's a big deal right now.
>>>>
>>>> This seems related enough that I'd like to fix it at the same time; why
>>>> doesn't your patch fix it?  Is it because typename_p is false?
>>>
>>> Ah, I was mistaken.  Of course we need the template keyword here: it's a member of an
>>> unknown specialization!
>>
>> That's why it's needed in contexts where we don't know whether or not we're
>> naming a type.  But where we do know that, as in a base-specifier, it isn't
>> necessary.  This is exactly DR 1710: "The keyword template is optional in a
>> ... class-or-decltype (Clause 11.7 [class.derived])...."
> 
> Duh, not sure what I was thinking.
> 
> I've implemented that in cp_parser_nested_name_specifier_opt: assume 'template'
> if we've seen 'typename'.  If it turns out that this is wrong because it
> triggers in contexts where it shouldn't, we'll have to introduce something like
> CP_PARSER_FLAGS_TEMPLATE_OPTIONAL.
> 
> With this another problem revealed: we weren't accepting an alias template
> after 'template' which DR1710 says is valid.  Fixed by the TYPE_ALIAS_P hunk
> in the new check_template_keyword_in_nested_name_spec. (alias-decl1.C)
> 
> But this is still not over: I noticed that
> 
> template <typename T> struct S {
>    template <typename TT>
>    using U = TT;
> };
> template <typename T> typename S<int>::template U<T>::type foo;
> 
> is still rejected and shouldn't be.  Here check_template_keyword_in_nested_name_spec
> gets
> 
>   <template_type_parm 0x7fffeaa42690 U type_0 VOID
>      align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fffeaa420a8
>     index 0 level 1 orig_level 2
>      chain <type_decl 0x7fffea90bda8 TT>>
> 
> Any suggestions how to handle this?

It should work to check for an alias whether or not the target is a 
TYPENAME_TYPE.

> FWIW, bootstrapped/regtested on x86_64-linux.
> 
> -- >8 --
> Consider
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(typename A<T>::B<U>);
>      };
>    };
> 
> which is rejected with
> error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
> whereas clang/icc/msvc accept it.
> 
> "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
> don't mention it anywhere, because the typename-specifier wasn't in C++11;
> it was only added to the language in N1376.  Instead, we handle it as
> an elaborated-type-specifier (not a problem thus far).   So we get to
> cp_parser_nested_name_specifier_opt which has a loop that breaks if we
> don't see a < or ::, but that means we can -- tentatively -- parse even
> B<U> which is not a nested-name-specifier (it doesn't end with a ::).
> 
> Even though we're parsing B<U> tentatively, we issue an error in
> cp_parser_class_name -> make_typename_type, but here we should not.  In
> fact, we probably shouldn't have parsed "B<U>" at all.  Fixed by the
> cp_parser_class_name hunk.
> 
> I think this should compile because [temp.names]/4 says: "In a qualified-id
> used as the name in a typename-specifier, elaborated-type-specifier,
> using-declaration, or class-or-decltype, an optional keyword template
> appearing at the top level is ignored.", added in DR 1710.  Also see
> DR 1812.
> 
> This issue on its own is not a significant problem or a regression.
> However, in C++20, the typename here becomes optional, and so this test
> is rejected in C++20, but accepted in C++17:
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(A<T>::B<U>);
>      };
>    };
> 
> Here we morph A<T>::B<U> into a typename-specifier, but that happens
> in cp_parser_simple_type_specifier and we never handle it as above.
> To fake the template keyword I'm afraid we need to use cp_parser_template_id
> with template_keyword_p=true as in the patch below.  The tricky thing
> is to avoid breaking concepts.
> 
> To handle DR 1710, I made cp_parser_nested_name_specifier_opt assume that
> when we're naming a type, the template keyword is present, too.  That
> revealed a bug: DR 1710 also says that the template keyword can be followed
> by an alias template, but we weren't prepared to handle that.  alias-decl1.C
> exercise this.
> 
> 	DR 1710
> 	PR c++/94057 - template keyword in a typename-specifier.
> 	* parser.c (check_template_keyword_in_nested_name_spec): New.
> 	(cp_parser_nested_name_specifier_opt): Implement DR1710, optional
> 	'template'.  Call check_template_keyword_in_nested_name_spec.
> 	(cp_parser_simple_type_specifier): Assume that a <
> 	following a qualified-id in a typename-specifier begins
> 	a template argument list.
> 	(cp_parser_class_name): Complain only if not parsing tentatively.
> 
> 	* g++.dg/cpp1y/alias-decl1.C: New test.
> 	* g++.dg/parse/missing-template1.C: Update dg-error.
> 	* g++.dg/parse/template3.C: Likewise.
> 	* g++.dg/template/error4.C: Likewise.
> 	* g++.dg/template/meminit2.C: Likewise.
> 	* g++.dg/template/dependent-name5.C: Likewise.
> 	* g++.dg/template/dependent-name7.C: New test.
> 	* g++.dg/template/dependent-name8.C: New test.
> 	* g++.dg/template/dependent-name9.C: New test.
> 	* g++.dg/template/dependent-name10.C: New test.
> 	* g++.dg/template/dependent-name11.C: New test.
> 	* g++.dg/template/dependent-name12.C: New test.
> 	* g++.dg/template/dependent-name13.C: New test.
> 	* g++.dg/template/dr1794.C: New test.
> 	* g++.dg/template/dr314.C: New test.
> 	* g++.dg/template/dr1710.C: New test.
> 	* g++.dg/template/dr1710-2.C: New test.
> 	* g++.old-deja/g++.pt/crash38.C: Update dg-error.
> ---
>   gcc/cp/parser.c                               | 91 ++++++++++++++++---
>   gcc/testsuite/g++.dg/cpp1y/alias-decl1.C      |  9 ++
>   .../g++.dg/parse/missing-template1.C          |  4 +-
>   gcc/testsuite/g++.dg/parse/template3.C        |  5 +-
>   .../g++.dg/template/dependent-name10.C        | 18 ++++
>   .../g++.dg/template/dependent-name11.C        | 15 +++
>   .../g++.dg/template/dependent-name12.C        |  7 ++
>   .../g++.dg/template/dependent-name13.C        |  8 ++
>   .../g++.dg/template/dependent-name5.C         |  2 -
>   .../g++.dg/template/dependent-name7.C         |  9 ++
>   .../g++.dg/template/dependent-name8.C         |  9 ++
>   .../g++.dg/template/dependent-name9.C         |  9 ++
>   gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 ++
>   gcc/testsuite/g++.dg/template/dr1710.C        |  9 ++
>   gcc/testsuite/g++.dg/template/dr1794.C        | 14 +++
>   gcc/testsuite/g++.dg/template/dr314.C         | 15 +++
>   gcc/testsuite/g++.dg/template/error4.C        |  3 +-
>   gcc/testsuite/g++.dg/template/meminit2.C      |  4 +-
>   gcc/testsuite/g++.old-deja/g++.pt/crash38.C   |  6 +-
>   19 files changed, 214 insertions(+), 33 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name12.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name13.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr314.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index cbd5510a8fb..824fb18e83a 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -6266,6 +6266,39 @@ cp_parser_unqualified_id (cp_parser* parser,
>       }
>   }
>   
> +/* Check [temp.names]/5: A name prefixed by the keyword template shall
> +   be a template-id or the name shall refer to a class template or an
> +   alias template.  */
> +
> +static void
> +check_template_keyword_in_nested_name_spec (tree name)
> +{
> +  if (CLASS_TYPE_P (name)
> +      && ((CLASSTYPE_USE_TEMPLATE (name)
> +	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
> +	  || CLASSTYPE_IS_TEMPLATE (name)))
> +    return;
> +
> +
> +  if (TREE_CODE (name) == TYPENAME_TYPE)
> +    {
> +      if (TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
> +	return;
> +      /* Alias templates are also OK.  */
> +      else if (TYPE_ALIAS_P (name))
> +	{
> +	  tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (name);
> +	  if (tinfo && DECL_ALIAS_TEMPLATE_P (TI_TEMPLATE (tinfo)))
> +	    return;
> +	}
> +    }

I think you want to check all typedefs like in e.g. 
find_parameter_packs_r; if the name is a typedef, it's only suitable if 
alias_template_specialization_p.

> +  permerror (input_location, TYPE_P (name)
> +	     ? G_("%qT is not a template")
> +	     : G_("%qD is not a template"),
> +	     name);
> +}
> +
>   /* Parse an (optional) nested-name-specifier.
>   
>      nested-name-specifier: [C++98]
> @@ -6389,7 +6422,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>         /* Look for the optional `template' keyword, if this isn't the
>   	 first time through the loop.  */
>         if (success)
> -	template_keyword_p = cp_parser_optional_template_keyword (parser);
> +	{
> +	  template_keyword_p = cp_parser_optional_template_keyword (parser);
> +	  /* DR1710: "In a qualified-id used as the name in
> +	     a typename-specifier, elaborated-type-specifier, using-declaration,
> +	     or class-or-decltype, an optional keyword template appearing at
> +	     the top level is ignored."  */
> +	  if (!template_keyword_p
> +	      && typename_keyword_p
> +	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
> +	    template_keyword_p = true;
> +	}
>   
>         /* Save the old scope since the name lookup we are about to do
>   	 might destroy it.  */
> @@ -6580,18 +6623,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>         if (TREE_CODE (new_scope) == TYPE_DECL)
>   	new_scope = TREE_TYPE (new_scope);
>         /* Uses of "template" must be followed by actual templates.  */
> -      if (template_keyword_p
> -	  && !(CLASS_TYPE_P (new_scope)
> -	       && ((CLASSTYPE_USE_TEMPLATE (new_scope)
> -		    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope)))
> -		   || CLASSTYPE_IS_TEMPLATE (new_scope)))
> -	  && !(TREE_CODE (new_scope) == TYPENAME_TYPE
> -	       && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope))
> -		   == TEMPLATE_ID_EXPR)))
> -	permerror (input_location, TYPE_P (new_scope)
> -		   ? G_("%qT is not a template")
> -		   : G_("%qD is not a template"),
> -		   new_scope);
> +      if (template_keyword_p)
> +	check_template_keyword_in_nested_name_spec (new_scope);
>         /* If it is a class scope, try to complete it; we are about to
>   	 be looking up names inside the class.  */
>         if (TYPE_P (new_scope)
> @@ -18113,6 +18146,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>   		}
>   	    }
>   	}
> +      /* DR 1812: A < following a qualified-id in a typename-specifier
> +	 could safely be assumed to begin a template argument list, so
> +	 the template keyword should be optional.  */
> +      else if (parser->scope
> +	       && qualified_p
> +	       && typename_p
> +	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
> +	{
> +	  cp_parser_parse_tentatively (parser);
> +
> +	  type = cp_parser_template_id (parser,
> +					/*template_keyword_p=*/true,
> +					/*check_dependency_p=*/true,
> +					none_type,
> +					/*is_declaration=*/false);
> +	  /* This is handled below, so back off.  */
> +	  if (type && concept_check_p (type))
> +	    cp_parser_simulate_error (parser);
> +
> +	  if (!cp_parser_parse_definitely (parser))
> +	    type = NULL_TREE;
> +	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
> +	    type = make_typename_type (parser->scope, type, typename_type,
> +				       /*complain=*/tf_error);
> +	  else if (TREE_CODE (type) != TYPE_DECL)
> +	    type = NULL_TREE;
> +	}
>   
>         /* Otherwise, look for a type-name.  */
>         if (!type)
> @@ -23636,8 +23696,9 @@ cp_parser_class_name (cp_parser *parser,
>         && decl != error_mark_node
>         && !is_overloaded_fn (decl))
>       {
> -      decl = make_typename_type (scope, decl, typename_type,
> -				 /*complain=*/tf_error);
> +      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
> +				 ? tf_none : tf_error);
> +      decl = make_typename_type (scope, decl, typename_type, complain);

Why?

>         if (decl != error_mark_node)
>   	decl = TYPE_NAME (decl);
>       }
> diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
> new file mode 100644
> index 00000000000..e5397e71bd6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile { target c++14 } }
> +
> +template <int> struct S {
> +  template <int> struct A;
> +  template <int N> using U = typename A<N>::foo;
> +};
> +template <typename T> typename S<1>::U<T::foo>::type a;
> +template <typename T> typename S<1>::template U<T::foo>::type a2;
> diff --git a/gcc/testsuite/g++.dg/parse/missing-template1.C b/gcc/testsuite/g++.dg/parse/missing-template1.C
> index fcc3aa0c3ff..19c1433d09f 100644
> --- a/gcc/testsuite/g++.dg/parse/missing-template1.C
> +++ b/gcc/testsuite/g++.dg/parse/missing-template1.C
> @@ -12,9 +12,7 @@ template <typename T> struct A
>   
>   template <typename T> void foo()
>   {
> -    typedef typename A<T>::B<T>::X Y; // { dg-error "non-template" "non" }
> -    // { dg-error "not declare" "decl" { target *-*-* } .-1 }
> -    // { dg-message "note" "note" { target *-*-* } .-2 }
> +    typedef typename A<T>::B<T>::X Y;
>   }
>   
>   void bar()
> diff --git a/gcc/testsuite/g++.dg/parse/template3.C b/gcc/testsuite/g++.dg/parse/template3.C
> index c284a5ee040..8da8a48a3c3 100644
> --- a/gcc/testsuite/g++.dg/parse/template3.C
> +++ b/gcc/testsuite/g++.dg/parse/template3.C
> @@ -13,7 +13,4 @@ struct X : Outer<b>::template Inner<T>
>   {};
>   
>   template <bool b, typename T>
> -struct Y : Outer<b>::Inner<T> {}; // { dg-error "used as template" "temp" }
> -// { dg-error "expected" "exp" { target *-*-* } .-1 }
> -// { dg-message "note" "note" { target *-*-* } .-2 }
> -
> +struct Y : Outer<b>::Inner<T> {};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
> new file mode 100644
> index 00000000000..18e024f7e6d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
> @@ -0,0 +1,18 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    typedef int TT;
> +    typedef int TT2;
> +    typedef int TT3;
> +    typedef int TT4;
> +  };
> +};
> +
> +struct X : A<int>::B<int> {
> +  using A<int>::template B<int>::TT;
> +  using typename A<int>::template B<int>::TT2;
> +  using A<int>::B<int>::TT3;
> +  using typename A<int>::B<int>::TT4;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
> new file mode 100644
> index 00000000000..687a9bd5df5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
> @@ -0,0 +1,15 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U>
> +  struct W { };
> +};
> +
> +void
> +g ()
> +{
> +  // class-key nested-name-specifier template[opt] simple-template-id
> +  struct A<int>::W<int> w;
> +  struct A<int>::template W<int> w2;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name12.C b/gcc/testsuite/g++.dg/template/dependent-name12.C
> new file mode 100644
> index 00000000000..7ee94e7457d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name12.C
> @@ -0,0 +1,7 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +
> +template <bool> struct A;
> +template <typename, typename> struct B;
> +template <typename T, typename U, typename V> struct B<T U::*, V> {
> +  typename A<V::x>::type::type t;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name13.C b/gcc/testsuite/g++.dg/template/dependent-name13.C
> new file mode 100644
> index 00000000000..1e971168657
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name13.C
> @@ -0,0 +1,8 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +
> +template<typename T> struct S {
> +  void fn(typename T::template B<int>::template C<int>);
> +  void fn2(typename T::B<int>::template C<int>);
> +  void fn3(typename T::template B<int>::C<int>);
> +  void fn4(typename T::B<int>::C<int>);
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
> index fc78983324b..15c1acb0347 100644
> --- a/gcc/testsuite/g++.dg/template/dependent-name5.C
> +++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
> @@ -22,9 +22,7 @@ struct A
>   
>     typedef N<int>       type6;
>     typedef A::N<int>    type7;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T>::N<int> type8;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
>     typedef typename A<T*>::template N<int> type10;
>   
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
> new file mode 100644
> index 00000000000..3dfa42d2df0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(A<T>::B<U>&);
> +    void fn(A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
> new file mode 100644
> index 00000000000..ad9e44f9b85
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::B<U>&);
> +    void fn(typename A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
> new file mode 100644
> index 00000000000..6dfdbc176c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::template B<U>&);
> +    void fn(typename A<T>::template B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
> new file mode 100644
> index 00000000000..99d49b746b2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
> @@ -0,0 +1,10 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +  };
> +};
> +
> +template<typename T> struct D : A<int>::B<int> {};
> +template<typename T> struct D2 : A<int>::template B<int> {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
> new file mode 100644
> index 00000000000..c945977971f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct D : T::template B<int>::template C<int> {};
> +template<typename T> struct D2 : T::B<int>::template C<int> {};
> +template<typename T> struct D3 : T::template B<int>::C<int> {};
> +template<typename T> struct D4 : T::B<int>::C<int> {};
> +template<typename T> struct D5 : T::template B<int>::type::type {};
> +template<typename T> struct D6 : T::B<int>::type::type {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
> new file mode 100644
> index 00000000000..f629d7d0b98
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1794.C
> @@ -0,0 +1,14 @@
> +// DR 1794 - template keyword and alias templates.
> +// { dg-do compile { target c++11 } }
> +
> +template<template<typename> class Template>
> +struct Internal {
> +  template<typename Arg>
> +  using Bind = Template<Arg>;
> +};
> +
> +template<template<typename> class Template, typename Arg>
> +using Instantiate = Template<Arg>;
> +
> +template<template<typename> class Template, typename Argument>
> +using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
> diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
> new file mode 100644
> index 00000000000..7c0d8ac592f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr314.C
> @@ -0,0 +1,15 @@
> +// DR 314 - template in base class specifier.
> +
> +template <typename T>
> +struct A {
> +  template <typename U>
> +  struct B {};
> +};
> +
> +template <typename T>
> +struct C : public A<T>::template B<T> {
> +};
> +
> +template <typename T>
> +struct C2 : public A<int>::B<T> {
> +};
> diff --git a/gcc/testsuite/g++.dg/template/error4.C b/gcc/testsuite/g++.dg/template/error4.C
> index 9d76561aa02..a5030f06c98 100644
> --- a/gcc/testsuite/g++.dg/template/error4.C
> +++ b/gcc/testsuite/g++.dg/template/error4.C
> @@ -5,5 +5,4 @@ template<class T> struct C1
>   };
>   
>   template<class T, class U>
> -void foo(typename C1<T>::C2<U>::Type *) { } // { dg-error "template" "error " }
> -// { dg-message "note" "note" { target *-*-* } .-1 }
> +void foo(typename C1<T>::C2<U>::Type *) { }
> diff --git a/gcc/testsuite/g++.dg/template/meminit2.C b/gcc/testsuite/g++.dg/template/meminit2.C
> index db6e0427fc7..6a56a806746 100644
> --- a/gcc/testsuite/g++.dg/template/meminit2.C
> +++ b/gcc/testsuite/g++.dg/template/meminit2.C
> @@ -14,8 +14,6 @@ struct A : typename O<T>::template I<int> {   // { dg-error "keyword 'typename'
>   
>   template <typename T>
>   struct B : O<T>::template I<int> {
> -  B() :    O<T>::I<int>()   // { dg-error "used as template|it is a template" }
> +  B() :    O<T>::I<int>()
>     {}
>   };
> -
> -// { dg-bogus "end of input" "bogus token skipping in the parser" { xfail *-*-* } 17 }
> diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> index 5d386132436..60cd5e3f44e 100644
> --- a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> +++ b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> @@ -3,10 +3,8 @@
>   
>   template <class T>
>   struct S {
> -  typedef typename T::Y<T>::Z X; // { dg-error "non-template" "non-template" } No Y in A
> -// { dg-message "note" "note" { target *-*-* } .-1 }
> -// { dg-error "does not declare" "not declare" { target *-*-* } .-2 }
> -  X x; // { dg-error "does not name a type" } No Y in A
> +  typedef typename T::Y<T>::Z X; // { dg-error "not a class template" } No Y in A
> +  X x;
>   };
>   
>   struct A {
> 
> base-commit: c2211a60ff05b7a0289d3e287e72c181bb4d5d8b
>
Li, Pan2 via Gcc-patches March 25, 2020, 9:36 p.m. UTC | #8
On Wed, Mar 25, 2020 at 12:00:24AM -0400, Jason Merrill wrote:
> On 3/24/20 11:45 AM, Marek Polacek wrote:
> > On Mon, Mar 23, 2020 at 10:41:28AM -0400, Jason Merrill wrote:
> > > On 3/20/20 7:02 PM, Marek Polacek wrote:
> > > > On Fri, Mar 20, 2020 at 02:12:49PM -0400, Jason Merrill wrote:
> > > > > On 3/20/20 1:06 PM, Marek Polacek wrote:
> > > > > > Wonderful.  I've added a bunch of tests, and some from the related DRs too.
> > > > > > But I had a problem with the class-or-decltype case: if we have
> > > > > > 
> > > > > > template<typename T> struct D : T::template B<int>::template C<int> {};
> > > > > > 
> > > > > > then we still require all the 'template' keywords here (as does clang).  So I'm
> > > > > > kind of confused, but I don't think it's a big deal right now.
> > > > > 
> > > > > This seems related enough that I'd like to fix it at the same time; why
> > > > > doesn't your patch fix it?  Is it because typename_p is false?
> > > > 
> > > > Ah, I was mistaken.  Of course we need the template keyword here: it's a member of an
> > > > unknown specialization!
> > > 
> > > That's why it's needed in contexts where we don't know whether or not we're
> > > naming a type.  But where we do know that, as in a base-specifier, it isn't
> > > necessary.  This is exactly DR 1710: "The keyword template is optional in a
> > > ... class-or-decltype (Clause 11.7 [class.derived])...."
> > 
> > Duh, not sure what I was thinking.
> > 
> > I've implemented that in cp_parser_nested_name_specifier_opt: assume 'template'
> > if we've seen 'typename'.  If it turns out that this is wrong because it
> > triggers in contexts where it shouldn't, we'll have to introduce something like
> > CP_PARSER_FLAGS_TEMPLATE_OPTIONAL.
> > 
> > With this another problem revealed: we weren't accepting an alias template
> > after 'template' which DR1710 says is valid.  Fixed by the TYPE_ALIAS_P hunk
> > in the new check_template_keyword_in_nested_name_spec. (alias-decl1.C)
> > 
> > But this is still not over: I noticed that
> > 
> > template <typename T> struct S {
> >    template <typename TT>
> >    using U = TT;
> > };
> > template <typename T> typename S<int>::template U<T>::type foo;
> > 
> > is still rejected and shouldn't be.  Here check_template_keyword_in_nested_name_spec
> > gets
> > 
> >   <template_type_parm 0x7fffeaa42690 U type_0 VOID
> >      align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fffeaa420a8
> >     index 0 level 1 orig_level 2
> >      chain <type_decl 0x7fffea90bda8 TT>>
> > 
> > Any suggestions how to handle this?
> 
> It should work to check for an alias whether or not the target is a
> TYPENAME_TYPE.

Hmm, we don't have a TYPENAME_TYPE here at all, but since that template_type_parm
is typedef_variant_p, it can be handled along with...

> > +static void
> > +check_template_keyword_in_nested_name_spec (tree name)
> > +{
> > +  if (CLASS_TYPE_P (name)
> > +      && ((CLASSTYPE_USE_TEMPLATE (name)
> > +	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
> > +	  || CLASSTYPE_IS_TEMPLATE (name)))
> > +    return;
> > +
> > +
> > +  if (TREE_CODE (name) == TYPENAME_TYPE)
> > +    {
> > +      if (TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
> > +	return;
> > +      /* Alias templates are also OK.  */
> > +      else if (TYPE_ALIAS_P (name))
> > +	{
> > +	  tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (name);
> > +	  if (tinfo && DECL_ALIAS_TEMPLATE_P (TI_TEMPLATE (tinfo)))
> > +	    return;
> > +	}
> > +    }
> 
> I think you want to check all typedefs like in e.g. find_parameter_packs_r;
> if the name is a typedef, it's only suitable if
> alias_template_specialization_p.

..this: Since alias_template_specialization_p already checks TYPE_P and
typedef_variant_p, can we simply use that?

> >   /* Parse an (optional) nested-name-specifier.
> >      nested-name-specifier: [C++98]
> > @@ -6389,7 +6422,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
> >         /* Look for the optional `template' keyword, if this isn't the
> >   	 first time through the loop.  */
> >         if (success)
> > -	template_keyword_p = cp_parser_optional_template_keyword (parser);
> > +	{
> > +	  template_keyword_p = cp_parser_optional_template_keyword (parser);
> > +	  /* DR1710: "In a qualified-id used as the name in
> > +	     a typename-specifier, elaborated-type-specifier, using-declaration,
> > +	     or class-or-decltype, an optional keyword template appearing at
> > +	     the top level is ignored."  */
> > +	  if (!template_keyword_p
> > +	      && typename_keyword_p
> > +	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
> > +	    template_keyword_p = true;
> > +	}

And since we now do this, we don't need...

> > @@ -23636,8 +23696,9 @@ cp_parser_class_name (cp_parser *parser,
> >         && decl != error_mark_node
> >         && !is_overloaded_fn (decl))
> >       {
> > -      decl = make_typename_type (scope, decl, typename_type,
> > -				 /*complain=*/tf_error);
> > +      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
> > +				 ? tf_none : tf_error);
> > +      decl = make_typename_type (scope, decl, typename_type, complain);
> 
> Why?

...this hunk, so dropped.  New test alias-decl2.C added, otherwise no changes.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

-- >8 --
Consider

  template <typename T> class A {
    template <typename U> class B {
      void fn(typename A<T>::B<U>);
    };
  };

which is rejected with
error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
whereas clang/icc/msvc accept it.

"typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
don't mention it anywhere, because the typename-specifier wasn't in C++11;
it was only added to the language in N1376.  Instead, we handle it as
an elaborated-type-specifier (not a problem thus far).   So we get to
cp_parser_nested_name_specifier_opt which has a loop that breaks if we
don't see a < or ::, but that means we can -- tentatively -- parse even
B<U> which is not a nested-name-specifier (it doesn't end with a ::).

I think this should compile because [temp.names]/4 says: "In a qualified-id
used as the name in a typename-specifier, elaborated-type-specifier,
using-declaration, or class-or-decltype, an optional keyword template
appearing at the top level is ignored.", added in DR 1710.  Also see
DR 1812.

This issue on its own is not a significant problem or a regression.
However, in C++20, the typename here becomes optional, and so this test
is rejected in C++20, but accepted in C++17:

  template <typename T> class A {
    template <typename U> class B {
      void fn(A<T>::B<U>);
    };
  };

Here we morph A<T>::B<U> into a typename-specifier, but that happens
in cp_parser_simple_type_specifier and we never handle it as above.
To fake the template keyword I'm afraid we need to use cp_parser_template_id
with template_keyword_p=true as in the patch below.  The tricky thing
is to avoid breaking concepts.

To handle DR 1710, I made cp_parser_nested_name_specifier_opt assume that
when we're naming a type, the template keyword is present, too.  That
revealed a bug: DR 1710 also says that the template keyword can be followed
by an alias template, but we weren't prepared to handle that.  alias-decl?.C
exercise this.

	DR 1710
	PR c++/94057 - template keyword in a typename-specifier.
	* parser.c (check_template_keyword_in_nested_name_spec): New.
	(cp_parser_nested_name_specifier_opt): Implement DR1710, optional
	'template'.  Call check_template_keyword_in_nested_name_spec.
	(cp_parser_simple_type_specifier): Assume that a <
	following a qualified-id in a typename-specifier begins
	a template argument list.

	* g++.dg/cpp1y/alias-decl1.C: New test.
	* g++.dg/cpp1y/alias-decl2.C: New test.
	* g++.dg/parse/missing-template1.C: Update dg-error.
	* g++.dg/parse/template3.C: Likewise.
	* g++.dg/template/error4.C: Likewise.
	* g++.dg/template/meminit2.C: Likewise.
	* g++.dg/template/dependent-name5.C: Likewise.
	* g++.dg/template/dependent-name7.C: New test.
	* g++.dg/template/dependent-name8.C: New test.
	* g++.dg/template/dependent-name9.C: New test.
	* g++.dg/template/dependent-name10.C: New test.
	* g++.dg/template/dependent-name11.C: New test.
	* g++.dg/template/dependent-name12.C: New test.
	* g++.dg/template/dependent-name13.C: New test.
	* g++.dg/template/dr1794.C: New test.
	* g++.dg/template/dr314.C: New test.
	* g++.dg/template/dr1710.C: New test.
	* g++.dg/template/dr1710-2.C: New test.
	* g++.old-deja/g++.pt/crash38.C: Update dg-error.
---
 gcc/cp/parser.c                               | 79 ++++++++++++++++---
 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C      |  9 +++
 gcc/testsuite/g++.dg/cpp1y/alias-decl2.C      |  8 ++
 .../g++.dg/parse/missing-template1.C          |  4 +-
 gcc/testsuite/g++.dg/parse/template3.C        |  5 +-
 .../g++.dg/template/dependent-name10.C        | 18 +++++
 .../g++.dg/template/dependent-name11.C        | 15 ++++
 .../g++.dg/template/dependent-name12.C        |  7 ++
 .../g++.dg/template/dependent-name13.C        |  8 ++
 .../g++.dg/template/dependent-name5.C         |  2 -
 .../g++.dg/template/dependent-name7.C         |  9 +++
 .../g++.dg/template/dependent-name8.C         |  9 +++
 .../g++.dg/template/dependent-name9.C         |  9 +++
 gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 +++
 gcc/testsuite/g++.dg/template/dr1710.C        |  9 +++
 gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++
 gcc/testsuite/g++.dg/template/dr314.C         | 15 ++++
 gcc/testsuite/g++.dg/template/error4.C        |  3 +-
 gcc/testsuite/g++.dg/template/meminit2.C      |  4 +-
 gcc/testsuite/g++.old-deja/g++.pt/crash38.C   |  6 +-
 20 files changed, 212 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name12.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name13.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr314.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 05363653691..753b3148ab3 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -6266,6 +6266,32 @@ cp_parser_unqualified_id (cp_parser* parser,
     }
 }
 
+/* Check [temp.names]/5: A name prefixed by the keyword template shall
+   be a template-id or the name shall refer to a class template or an
+   alias template.  */
+
+static void
+check_template_keyword_in_nested_name_spec (tree name)
+{
+  if (CLASS_TYPE_P (name)
+      && ((CLASSTYPE_USE_TEMPLATE (name)
+	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
+	  || CLASSTYPE_IS_TEMPLATE (name)))
+    return;
+
+  if (TREE_CODE (name) == TYPENAME_TYPE
+      && TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
+    return;
+  /* Alias templates are also OK.  */
+  else if (alias_template_specialization_p (name, nt_opaque))
+    return;
+
+  permerror (input_location, TYPE_P (name)
+	     ? G_("%qT is not a template")
+	     : G_("%qD is not a template"),
+	     name);
+}
+
 /* Parse an (optional) nested-name-specifier.
 
    nested-name-specifier: [C++98]
@@ -6389,7 +6415,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
       /* Look for the optional `template' keyword, if this isn't the
 	 first time through the loop.  */
       if (success)
-	template_keyword_p = cp_parser_optional_template_keyword (parser);
+	{
+	  template_keyword_p = cp_parser_optional_template_keyword (parser);
+	  /* DR1710: "In a qualified-id used as the name in
+	     a typename-specifier, elaborated-type-specifier, using-declaration,
+	     or class-or-decltype, an optional keyword template appearing at
+	     the top level is ignored."  */
+	  if (!template_keyword_p
+	      && typename_keyword_p
+	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
+	    template_keyword_p = true;
+	}
 
       /* Save the old scope since the name lookup we are about to do
 	 might destroy it.  */
@@ -6580,18 +6616,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
       if (TREE_CODE (new_scope) == TYPE_DECL)
 	new_scope = TREE_TYPE (new_scope);
       /* Uses of "template" must be followed by actual templates.  */
-      if (template_keyword_p
-	  && !(CLASS_TYPE_P (new_scope)
-	       && ((CLASSTYPE_USE_TEMPLATE (new_scope)
-		    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope)))
-		   || CLASSTYPE_IS_TEMPLATE (new_scope)))
-	  && !(TREE_CODE (new_scope) == TYPENAME_TYPE
-	       && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope))
-		   == TEMPLATE_ID_EXPR)))
-	permerror (input_location, TYPE_P (new_scope)
-		   ? G_("%qT is not a template")
-		   : G_("%qD is not a template"),
-		   new_scope);
+      if (template_keyword_p)
+	check_template_keyword_in_nested_name_spec (new_scope);
       /* If it is a class scope, try to complete it; we are about to
 	 be looking up names inside the class.  */
       if (TYPE_P (new_scope)
@@ -18120,6 +18146,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 		}
 	    }
 	}
+      /* DR 1812: A < following a qualified-id in a typename-specifier
+	 could safely be assumed to begin a template argument list, so
+	 the template keyword should be optional.  */
+      else if (parser->scope
+	       && qualified_p
+	       && typename_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
+	{
+	  cp_parser_parse_tentatively (parser);
+
+	  type = cp_parser_template_id (parser,
+					/*template_keyword_p=*/true,
+					/*check_dependency_p=*/true,
+					none_type,
+					/*is_declaration=*/false);
+	  /* This is handled below, so back off.  */
+	  if (type && concept_check_p (type))
+	    cp_parser_simulate_error (parser);
+
+	  if (!cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
+	    type = make_typename_type (parser->scope, type, typename_type,
+				       /*complain=*/tf_error);
+	  else if (TREE_CODE (type) != TYPE_DECL)
+	    type = NULL_TREE;
+	}
 
       /* Otherwise, look for a type-name.  */
       if (!type)
diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
new file mode 100644
index 00000000000..e5397e71bd6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile { target c++14 } }
+
+template <int> struct S {
+  template <int> struct A;
+  template <int N> using U = typename A<N>::foo;
+};
+template <typename T> typename S<1>::U<T::foo>::type a;
+template <typename T> typename S<1>::template U<T::foo>::type a2;
diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
new file mode 100644
index 00000000000..f42b425a30b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
@@ -0,0 +1,8 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile { target c++14 } }
+
+template <typename T> struct S {
+  template <typename TT>
+  using U = TT;
+};
+template <typename T> typename S<int>::template U<T>::type foo;
diff --git a/gcc/testsuite/g++.dg/parse/missing-template1.C b/gcc/testsuite/g++.dg/parse/missing-template1.C
index fcc3aa0c3ff..19c1433d09f 100644
--- a/gcc/testsuite/g++.dg/parse/missing-template1.C
+++ b/gcc/testsuite/g++.dg/parse/missing-template1.C
@@ -12,9 +12,7 @@ template <typename T> struct A
 
 template <typename T> void foo()
 {
-    typedef typename A<T>::B<T>::X Y; // { dg-error "non-template" "non" }
-    // { dg-error "not declare" "decl" { target *-*-* } .-1 }
-    // { dg-message "note" "note" { target *-*-* } .-2 }
+    typedef typename A<T>::B<T>::X Y;
 }
 
 void bar()
diff --git a/gcc/testsuite/g++.dg/parse/template3.C b/gcc/testsuite/g++.dg/parse/template3.C
index c284a5ee040..8da8a48a3c3 100644
--- a/gcc/testsuite/g++.dg/parse/template3.C
+++ b/gcc/testsuite/g++.dg/parse/template3.C
@@ -13,7 +13,4 @@ struct X : Outer<b>::template Inner<T>
 {};
 
 template <bool b, typename T>
-struct Y : Outer<b>::Inner<T> {}; // { dg-error "used as template" "temp" }
-// { dg-error "expected" "exp" { target *-*-* } .-1 }
-// { dg-message "note" "note" { target *-*-* } .-2 }
-
+struct Y : Outer<b>::Inner<T> {};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
new file mode 100644
index 00000000000..18e024f7e6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
@@ -0,0 +1,18 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    typedef int TT;
+    typedef int TT2;
+    typedef int TT3;
+    typedef int TT4;
+  };
+};
+
+struct X : A<int>::B<int> {
+  using A<int>::template B<int>::TT;
+  using typename A<int>::template B<int>::TT2;
+  using A<int>::B<int>::TT3;
+  using typename A<int>::B<int>::TT4;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
new file mode 100644
index 00000000000..687a9bd5df5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
@@ -0,0 +1,15 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U>
+  struct W { };
+};
+
+void
+g ()
+{
+  // class-key nested-name-specifier template[opt] simple-template-id
+  struct A<int>::W<int> w;
+  struct A<int>::template W<int> w2;
+}
diff --git a/gcc/testsuite/g++.dg/template/dependent-name12.C b/gcc/testsuite/g++.dg/template/dependent-name12.C
new file mode 100644
index 00000000000..7ee94e7457d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name12.C
@@ -0,0 +1,7 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+
+template <bool> struct A;
+template <typename, typename> struct B;
+template <typename T, typename U, typename V> struct B<T U::*, V> {
+  typename A<V::x>::type::type t;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name13.C b/gcc/testsuite/g++.dg/template/dependent-name13.C
new file mode 100644
index 00000000000..1e971168657
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name13.C
@@ -0,0 +1,8 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+
+template<typename T> struct S {
+  void fn(typename T::template B<int>::template C<int>);
+  void fn2(typename T::B<int>::template C<int>);
+  void fn3(typename T::template B<int>::C<int>);
+  void fn4(typename T::B<int>::C<int>);
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
index fc78983324b..15c1acb0347 100644
--- a/gcc/testsuite/g++.dg/template/dependent-name5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -22,9 +22,7 @@ struct A
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
new file mode 100644
index 00000000000..3dfa42d2df0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(A<T>::B<U>&);
+    void fn(A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
new file mode 100644
index 00000000000..ad9e44f9b85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::B<U>&);
+    void fn(typename A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
new file mode 100644
index 00000000000..6dfdbc176c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::template B<U>&);
+    void fn(typename A<T>::template B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
new file mode 100644
index 00000000000..99d49b746b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
@@ -0,0 +1,10 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+  };
+};
+
+template<typename T> struct D : A<int>::B<int> {};
+template<typename T> struct D2 : A<int>::template B<int> {};
diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
new file mode 100644
index 00000000000..c945977971f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct D : T::template B<int>::template C<int> {};
+template<typename T> struct D2 : T::B<int>::template C<int> {};
+template<typename T> struct D3 : T::template B<int>::C<int> {};
+template<typename T> struct D4 : T::B<int>::C<int> {};
+template<typename T> struct D5 : T::template B<int>::type::type {};
+template<typename T> struct D6 : T::B<int>::type::type {};
diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
new file mode 100644
index 00000000000..f629d7d0b98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1794.C
@@ -0,0 +1,14 @@
+// DR 1794 - template keyword and alias templates.
+// { dg-do compile { target c++11 } }
+
+template<template<typename> class Template>
+struct Internal {
+  template<typename Arg>
+  using Bind = Template<Arg>;
+};
+
+template<template<typename> class Template, typename Arg>
+using Instantiate = Template<Arg>;
+
+template<template<typename> class Template, typename Argument>
+using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
new file mode 100644
index 00000000000..7c0d8ac592f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr314.C
@@ -0,0 +1,15 @@
+// DR 314 - template in base class specifier.
+
+template <typename T>
+struct A {
+  template <typename U>
+  struct B {};
+};
+
+template <typename T>
+struct C : public A<T>::template B<T> {
+};
+
+template <typename T>
+struct C2 : public A<int>::B<T> {
+};
diff --git a/gcc/testsuite/g++.dg/template/error4.C b/gcc/testsuite/g++.dg/template/error4.C
index 9d76561aa02..a5030f06c98 100644
--- a/gcc/testsuite/g++.dg/template/error4.C
+++ b/gcc/testsuite/g++.dg/template/error4.C
@@ -5,5 +5,4 @@ template<class T> struct C1
 };
 
 template<class T, class U>
-void foo(typename C1<T>::C2<U>::Type *) { } // { dg-error "template" "error " }
-// { dg-message "note" "note" { target *-*-* } .-1 }
+void foo(typename C1<T>::C2<U>::Type *) { }
diff --git a/gcc/testsuite/g++.dg/template/meminit2.C b/gcc/testsuite/g++.dg/template/meminit2.C
index db6e0427fc7..6a56a806746 100644
--- a/gcc/testsuite/g++.dg/template/meminit2.C
+++ b/gcc/testsuite/g++.dg/template/meminit2.C
@@ -14,8 +14,6 @@ struct A : typename O<T>::template I<int> {   // { dg-error "keyword 'typename'
 
 template <typename T> 
 struct B : O<T>::template I<int> {
-  B() :    O<T>::I<int>()   // { dg-error "used as template|it is a template" }
+  B() :    O<T>::I<int>()
   {}
 };
-
-// { dg-bogus "end of input" "bogus token skipping in the parser" { xfail *-*-* } 17 }
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
index 5d386132436..60cd5e3f44e 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
@@ -3,10 +3,8 @@
 
 template <class T>
 struct S {
-  typedef typename T::Y<T>::Z X; // { dg-error "non-template" "non-template" } No Y in A
-// { dg-message "note" "note" { target *-*-* } .-1 }
-// { dg-error "does not declare" "not declare" { target *-*-* } .-2 }
-  X x; // { dg-error "does not name a type" } No Y in A
+  typedef typename T::Y<T>::Z X; // { dg-error "not a class template" } No Y in A
+  X x;
 };
 
 struct A {

base-commit: 48817fbd7616f086ac7bb1dd38b862f78762c9b8
Li, Pan2 via Gcc-patches March 26, 2020, 5:43 a.m. UTC | #9
On 3/25/20 5:36 PM, Marek Polacek wrote:
> On Wed, Mar 25, 2020 at 12:00:24AM -0400, Jason Merrill wrote:
>> On 3/24/20 11:45 AM, Marek Polacek wrote:
>>> On Mon, Mar 23, 2020 at 10:41:28AM -0400, Jason Merrill wrote:
>>>> On 3/20/20 7:02 PM, Marek Polacek wrote:
>>>>> On Fri, Mar 20, 2020 at 02:12:49PM -0400, Jason Merrill wrote:
>>>>>> On 3/20/20 1:06 PM, Marek Polacek wrote:
>>>>>>> Wonderful.  I've added a bunch of tests, and some from the related DRs too.
>>>>>>> But I had a problem with the class-or-decltype case: if we have
>>>>>>>
>>>>>>> template<typename T> struct D : T::template B<int>::template C<int> {};
>>>>>>>
>>>>>>> then we still require all the 'template' keywords here (as does clang).  So I'm
>>>>>>> kind of confused, but I don't think it's a big deal right now.
>>>>>>
>>>>>> This seems related enough that I'd like to fix it at the same time; why
>>>>>> doesn't your patch fix it?  Is it because typename_p is false?
>>>>>
>>>>> Ah, I was mistaken.  Of course we need the template keyword here: it's a member of an
>>>>> unknown specialization!
>>>>
>>>> That's why it's needed in contexts where we don't know whether or not we're
>>>> naming a type.  But where we do know that, as in a base-specifier, it isn't
>>>> necessary.  This is exactly DR 1710: "The keyword template is optional in a
>>>> ... class-or-decltype (Clause 11.7 [class.derived])...."
>>>
>>> Duh, not sure what I was thinking.
>>>
>>> I've implemented that in cp_parser_nested_name_specifier_opt: assume 'template'
>>> if we've seen 'typename'.  If it turns out that this is wrong because it
>>> triggers in contexts where it shouldn't, we'll have to introduce something like
>>> CP_PARSER_FLAGS_TEMPLATE_OPTIONAL.
>>>
>>> With this another problem revealed: we weren't accepting an alias template
>>> after 'template' which DR1710 says is valid.  Fixed by the TYPE_ALIAS_P hunk
>>> in the new check_template_keyword_in_nested_name_spec. (alias-decl1.C)
>>>
>>> But this is still not over: I noticed that
>>>
>>> template <typename T> struct S {
>>>     template <typename TT>
>>>     using U = TT;
>>> };
>>> template <typename T> typename S<int>::template U<T>::type foo;
>>>
>>> is still rejected and shouldn't be.  Here check_template_keyword_in_nested_name_spec
>>> gets
>>>
>>>    <template_type_parm 0x7fffeaa42690 U type_0 VOID
>>>       align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fffeaa420a8
>>>      index 0 level 1 orig_level 2
>>>       chain <type_decl 0x7fffea90bda8 TT>>
>>>
>>> Any suggestions how to handle this?
>>
>> It should work to check for an alias whether or not the target is a
>> TYPENAME_TYPE.
> 
> Hmm, we don't have a TYPENAME_TYPE here at all, but since that template_type_parm
> is typedef_variant_p, it can be handled along with...
> 
>>> +static void
>>> +check_template_keyword_in_nested_name_spec (tree name)
>>> +{
>>> +  if (CLASS_TYPE_P (name)
>>> +      && ((CLASSTYPE_USE_TEMPLATE (name)
>>> +	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
>>> +	  || CLASSTYPE_IS_TEMPLATE (name)))
>>> +    return;
>>> +
>>> +
>>> +  if (TREE_CODE (name) == TYPENAME_TYPE)
>>> +    {
>>> +      if (TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
>>> +	return;
>>> +      /* Alias templates are also OK.  */
>>> +      else if (TYPE_ALIAS_P (name))
>>> +	{
>>> +	  tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (name);
>>> +	  if (tinfo && DECL_ALIAS_TEMPLATE_P (TI_TEMPLATE (tinfo)))
>>> +	    return;
>>> +	}
>>> +    }
>>
>> I think you want to check all typedefs like in e.g. find_parameter_packs_r;
>> if the name is a typedef, it's only suitable if
>> alias_template_specialization_p.
> 
> ..this: Since alias_template_specialization_p already checks TYPE_P and
> typedef_variant_p, can we simply use that?

But not all typedefs are aliases; 'typedef A<int> type;' does not make 
'::template type' valid just because '::template A<int>' would be.  If 
it's an alias template specialization, OK; if it's any other typedef, 
not OK.

>>>    /* Parse an (optional) nested-name-specifier.
>>>       nested-name-specifier: [C++98]
>>> @@ -6389,7 +6422,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>>>          /* Look for the optional `template' keyword, if this isn't the
>>>    	 first time through the loop.  */
>>>          if (success)
>>> -	template_keyword_p = cp_parser_optional_template_keyword (parser);
>>> +	{
>>> +	  template_keyword_p = cp_parser_optional_template_keyword (parser);
>>> +	  /* DR1710: "In a qualified-id used as the name in
>>> +	     a typename-specifier, elaborated-type-specifier, using-declaration,
>>> +	     or class-or-decltype, an optional keyword template appearing at
>>> +	     the top level is ignored."  */
>>> +	  if (!template_keyword_p
>>> +	      && typename_keyword_p
>>> +	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
>>> +	    template_keyword_p = true;
>>> +	}
> 
> And since we now do this, we don't need...
> 
>>> @@ -23636,8 +23696,9 @@ cp_parser_class_name (cp_parser *parser,
>>>          && decl != error_mark_node
>>>          && !is_overloaded_fn (decl))
>>>        {
>>> -      decl = make_typename_type (scope, decl, typename_type,
>>> -				 /*complain=*/tf_error);
>>> +      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
>>> +				 ? tf_none : tf_error);
>>> +      decl = make_typename_type (scope, decl, typename_type, complain);
>>
>> Why?
> 
> ...this hunk, so dropped.  New test alias-decl2.C added, otherwise no changes.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> -- >8 --
> Consider
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(typename A<T>::B<U>);
>      };
>    };
> 
> which is rejected with
> error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
> whereas clang/icc/msvc accept it.
> 
> "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
> don't mention it anywhere, because the typename-specifier wasn't in C++11;
> it was only added to the language in N1376.  Instead, we handle it as
> an elaborated-type-specifier (not a problem thus far).   So we get to
> cp_parser_nested_name_specifier_opt which has a loop that breaks if we
> don't see a < or ::, but that means we can -- tentatively -- parse even
> B<U> which is not a nested-name-specifier (it doesn't end with a ::).
> 
> I think this should compile because [temp.names]/4 says: "In a qualified-id
> used as the name in a typename-specifier, elaborated-type-specifier,
> using-declaration, or class-or-decltype, an optional keyword template
> appearing at the top level is ignored.", added in DR 1710.  Also see
> DR 1812.
> 
> This issue on its own is not a significant problem or a regression.
> However, in C++20, the typename here becomes optional, and so this test
> is rejected in C++20, but accepted in C++17:
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(A<T>::B<U>);
>      };
>    };
> 
> Here we morph A<T>::B<U> into a typename-specifier, but that happens
> in cp_parser_simple_type_specifier and we never handle it as above.
> To fake the template keyword I'm afraid we need to use cp_parser_template_id
> with template_keyword_p=true as in the patch below.  The tricky thing
> is to avoid breaking concepts.
> 
> To handle DR 1710, I made cp_parser_nested_name_specifier_opt assume that
> when we're naming a type, the template keyword is present, too.  That
> revealed a bug: DR 1710 also says that the template keyword can be followed
> by an alias template, but we weren't prepared to handle that.  alias-decl?.C
> exercise this.
> 
> 	DR 1710
> 	PR c++/94057 - template keyword in a typename-specifier.
> 	* parser.c (check_template_keyword_in_nested_name_spec): New.
> 	(cp_parser_nested_name_specifier_opt): Implement DR1710, optional
> 	'template'.  Call check_template_keyword_in_nested_name_spec.
> 	(cp_parser_simple_type_specifier): Assume that a <
> 	following a qualified-id in a typename-specifier begins
> 	a template argument list.
> 
> 	* g++.dg/cpp1y/alias-decl1.C: New test.
> 	* g++.dg/cpp1y/alias-decl2.C: New test.
> 	* g++.dg/parse/missing-template1.C: Update dg-error.
> 	* g++.dg/parse/template3.C: Likewise.
> 	* g++.dg/template/error4.C: Likewise.
> 	* g++.dg/template/meminit2.C: Likewise.
> 	* g++.dg/template/dependent-name5.C: Likewise.
> 	* g++.dg/template/dependent-name7.C: New test.
> 	* g++.dg/template/dependent-name8.C: New test.
> 	* g++.dg/template/dependent-name9.C: New test.
> 	* g++.dg/template/dependent-name10.C: New test.
> 	* g++.dg/template/dependent-name11.C: New test.
> 	* g++.dg/template/dependent-name12.C: New test.
> 	* g++.dg/template/dependent-name13.C: New test.
> 	* g++.dg/template/dr1794.C: New test.
> 	* g++.dg/template/dr314.C: New test.
> 	* g++.dg/template/dr1710.C: New test.
> 	* g++.dg/template/dr1710-2.C: New test.
> 	* g++.old-deja/g++.pt/crash38.C: Update dg-error.
> ---
>   gcc/cp/parser.c                               | 79 ++++++++++++++++---
>   gcc/testsuite/g++.dg/cpp1y/alias-decl1.C      |  9 +++
>   gcc/testsuite/g++.dg/cpp1y/alias-decl2.C      |  8 ++
>   .../g++.dg/parse/missing-template1.C          |  4 +-
>   gcc/testsuite/g++.dg/parse/template3.C        |  5 +-
>   .../g++.dg/template/dependent-name10.C        | 18 +++++
>   .../g++.dg/template/dependent-name11.C        | 15 ++++
>   .../g++.dg/template/dependent-name12.C        |  7 ++
>   .../g++.dg/template/dependent-name13.C        |  8 ++
>   .../g++.dg/template/dependent-name5.C         |  2 -
>   .../g++.dg/template/dependent-name7.C         |  9 +++
>   .../g++.dg/template/dependent-name8.C         |  9 +++
>   .../g++.dg/template/dependent-name9.C         |  9 +++
>   gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 +++
>   gcc/testsuite/g++.dg/template/dr1710.C        |  9 +++
>   gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++
>   gcc/testsuite/g++.dg/template/dr314.C         | 15 ++++
>   gcc/testsuite/g++.dg/template/error4.C        |  3 +-
>   gcc/testsuite/g++.dg/template/meminit2.C      |  4 +-
>   gcc/testsuite/g++.old-deja/g++.pt/crash38.C   |  6 +-
>   20 files changed, 212 insertions(+), 31 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name12.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name13.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr314.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 05363653691..753b3148ab3 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -6266,6 +6266,32 @@ cp_parser_unqualified_id (cp_parser* parser,
>       }
>   }
>   
> +/* Check [temp.names]/5: A name prefixed by the keyword template shall
> +   be a template-id or the name shall refer to a class template or an
> +   alias template.  */
> +
> +static void
> +check_template_keyword_in_nested_name_spec (tree name)
> +{
> +  if (CLASS_TYPE_P (name)
> +      && ((CLASSTYPE_USE_TEMPLATE (name)
> +	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
> +	  || CLASSTYPE_IS_TEMPLATE (name)))
> +    return;
> +
> +  if (TREE_CODE (name) == TYPENAME_TYPE
> +      && TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
> +    return;
> +  /* Alias templates are also OK.  */
> +  else if (alias_template_specialization_p (name, nt_opaque))
> +    return;
> +
> +  permerror (input_location, TYPE_P (name)
> +	     ? G_("%qT is not a template")
> +	     : G_("%qD is not a template"),
> +	     name);
> +}
> +
>   /* Parse an (optional) nested-name-specifier.
>   
>      nested-name-specifier: [C++98]
> @@ -6389,7 +6415,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>         /* Look for the optional `template' keyword, if this isn't the
>   	 first time through the loop.  */
>         if (success)
> -	template_keyword_p = cp_parser_optional_template_keyword (parser);
> +	{
> +	  template_keyword_p = cp_parser_optional_template_keyword (parser);
> +	  /* DR1710: "In a qualified-id used as the name in
> +	     a typename-specifier, elaborated-type-specifier, using-declaration,
> +	     or class-or-decltype, an optional keyword template appearing at
> +	     the top level is ignored."  */
> +	  if (!template_keyword_p
> +	      && typename_keyword_p
> +	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
> +	    template_keyword_p = true;
> +	}
>   
>         /* Save the old scope since the name lookup we are about to do
>   	 might destroy it.  */
> @@ -6580,18 +6616,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>         if (TREE_CODE (new_scope) == TYPE_DECL)
>   	new_scope = TREE_TYPE (new_scope);
>         /* Uses of "template" must be followed by actual templates.  */
> -      if (template_keyword_p
> -	  && !(CLASS_TYPE_P (new_scope)
> -	       && ((CLASSTYPE_USE_TEMPLATE (new_scope)
> -		    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope)))
> -		   || CLASSTYPE_IS_TEMPLATE (new_scope)))
> -	  && !(TREE_CODE (new_scope) == TYPENAME_TYPE
> -	       && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope))
> -		   == TEMPLATE_ID_EXPR)))
> -	permerror (input_location, TYPE_P (new_scope)
> -		   ? G_("%qT is not a template")
> -		   : G_("%qD is not a template"),
> -		   new_scope);
> +      if (template_keyword_p)
> +	check_template_keyword_in_nested_name_spec (new_scope);
>         /* If it is a class scope, try to complete it; we are about to
>   	 be looking up names inside the class.  */
>         if (TYPE_P (new_scope)
> @@ -18120,6 +18146,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>   		}
>   	    }
>   	}
> +      /* DR 1812: A < following a qualified-id in a typename-specifier
> +	 could safely be assumed to begin a template argument list, so
> +	 the template keyword should be optional.  */
> +      else if (parser->scope
> +	       && qualified_p
> +	       && typename_p
> +	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
> +	{
> +	  cp_parser_parse_tentatively (parser);
> +
> +	  type = cp_parser_template_id (parser,
> +					/*template_keyword_p=*/true,
> +					/*check_dependency_p=*/true,
> +					none_type,
> +					/*is_declaration=*/false);
> +	  /* This is handled below, so back off.  */
> +	  if (type && concept_check_p (type))
> +	    cp_parser_simulate_error (parser);
> +
> +	  if (!cp_parser_parse_definitely (parser))
> +	    type = NULL_TREE;
> +	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
> +	    type = make_typename_type (parser->scope, type, typename_type,
> +				       /*complain=*/tf_error);
> +	  else if (TREE_CODE (type) != TYPE_DECL)
> +	    type = NULL_TREE;
> +	}
>   
>         /* Otherwise, look for a type-name.  */
>         if (!type)
> diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
> new file mode 100644
> index 00000000000..e5397e71bd6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile { target c++14 } }
> +
> +template <int> struct S {
> +  template <int> struct A;
> +  template <int N> using U = typename A<N>::foo;
> +};
> +template <typename T> typename S<1>::U<T::foo>::type a;
> +template <typename T> typename S<1>::template U<T::foo>::type a2;
> diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
> new file mode 100644
> index 00000000000..f42b425a30b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
> @@ -0,0 +1,8 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile { target c++14 } }
> +
> +template <typename T> struct S {
> +  template <typename TT>
> +  using U = TT;
> +};
> +template <typename T> typename S<int>::template U<T>::type foo;
> diff --git a/gcc/testsuite/g++.dg/parse/missing-template1.C b/gcc/testsuite/g++.dg/parse/missing-template1.C
> index fcc3aa0c3ff..19c1433d09f 100644
> --- a/gcc/testsuite/g++.dg/parse/missing-template1.C
> +++ b/gcc/testsuite/g++.dg/parse/missing-template1.C
> @@ -12,9 +12,7 @@ template <typename T> struct A
>   
>   template <typename T> void foo()
>   {
> -    typedef typename A<T>::B<T>::X Y; // { dg-error "non-template" "non" }
> -    // { dg-error "not declare" "decl" { target *-*-* } .-1 }
> -    // { dg-message "note" "note" { target *-*-* } .-2 }
> +    typedef typename A<T>::B<T>::X Y;
>   }
>   
>   void bar()
> diff --git a/gcc/testsuite/g++.dg/parse/template3.C b/gcc/testsuite/g++.dg/parse/template3.C
> index c284a5ee040..8da8a48a3c3 100644
> --- a/gcc/testsuite/g++.dg/parse/template3.C
> +++ b/gcc/testsuite/g++.dg/parse/template3.C
> @@ -13,7 +13,4 @@ struct X : Outer<b>::template Inner<T>
>   {};
>   
>   template <bool b, typename T>
> -struct Y : Outer<b>::Inner<T> {}; // { dg-error "used as template" "temp" }
> -// { dg-error "expected" "exp" { target *-*-* } .-1 }
> -// { dg-message "note" "note" { target *-*-* } .-2 }
> -
> +struct Y : Outer<b>::Inner<T> {};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
> new file mode 100644
> index 00000000000..18e024f7e6d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
> @@ -0,0 +1,18 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    typedef int TT;
> +    typedef int TT2;
> +    typedef int TT3;
> +    typedef int TT4;
> +  };
> +};
> +
> +struct X : A<int>::B<int> {
> +  using A<int>::template B<int>::TT;
> +  using typename A<int>::template B<int>::TT2;
> +  using A<int>::B<int>::TT3;
> +  using typename A<int>::B<int>::TT4;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
> new file mode 100644
> index 00000000000..687a9bd5df5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
> @@ -0,0 +1,15 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U>
> +  struct W { };
> +};
> +
> +void
> +g ()
> +{
> +  // class-key nested-name-specifier template[opt] simple-template-id
> +  struct A<int>::W<int> w;
> +  struct A<int>::template W<int> w2;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name12.C b/gcc/testsuite/g++.dg/template/dependent-name12.C
> new file mode 100644
> index 00000000000..7ee94e7457d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name12.C
> @@ -0,0 +1,7 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +
> +template <bool> struct A;
> +template <typename, typename> struct B;
> +template <typename T, typename U, typename V> struct B<T U::*, V> {
> +  typename A<V::x>::type::type t;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name13.C b/gcc/testsuite/g++.dg/template/dependent-name13.C
> new file mode 100644
> index 00000000000..1e971168657
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name13.C
> @@ -0,0 +1,8 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +
> +template<typename T> struct S {
> +  void fn(typename T::template B<int>::template C<int>);
> +  void fn2(typename T::B<int>::template C<int>);
> +  void fn3(typename T::template B<int>::C<int>);
> +  void fn4(typename T::B<int>::C<int>);
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
> index fc78983324b..15c1acb0347 100644
> --- a/gcc/testsuite/g++.dg/template/dependent-name5.C
> +++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
> @@ -22,9 +22,7 @@ struct A
>   
>     typedef N<int>       type6;
>     typedef A::N<int>    type7;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T>::N<int> type8;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
>     typedef typename A<T*>::template N<int> type10;
>   
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
> new file mode 100644
> index 00000000000..3dfa42d2df0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(A<T>::B<U>&);
> +    void fn(A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
> new file mode 100644
> index 00000000000..ad9e44f9b85
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::B<U>&);
> +    void fn(typename A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
> new file mode 100644
> index 00000000000..6dfdbc176c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::template B<U>&);
> +    void fn(typename A<T>::template B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
> new file mode 100644
> index 00000000000..99d49b746b2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
> @@ -0,0 +1,10 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +  };
> +};
> +
> +template<typename T> struct D : A<int>::B<int> {};
> +template<typename T> struct D2 : A<int>::template B<int> {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
> new file mode 100644
> index 00000000000..c945977971f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct D : T::template B<int>::template C<int> {};
> +template<typename T> struct D2 : T::B<int>::template C<int> {};
> +template<typename T> struct D3 : T::template B<int>::C<int> {};
> +template<typename T> struct D4 : T::B<int>::C<int> {};
> +template<typename T> struct D5 : T::template B<int>::type::type {};
> +template<typename T> struct D6 : T::B<int>::type::type {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
> new file mode 100644
> index 00000000000..f629d7d0b98
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1794.C
> @@ -0,0 +1,14 @@
> +// DR 1794 - template keyword and alias templates.
> +// { dg-do compile { target c++11 } }
> +
> +template<template<typename> class Template>
> +struct Internal {
> +  template<typename Arg>
> +  using Bind = Template<Arg>;
> +};
> +
> +template<template<typename> class Template, typename Arg>
> +using Instantiate = Template<Arg>;
> +
> +template<template<typename> class Template, typename Argument>
> +using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
> diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
> new file mode 100644
> index 00000000000..7c0d8ac592f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr314.C
> @@ -0,0 +1,15 @@
> +// DR 314 - template in base class specifier.
> +
> +template <typename T>
> +struct A {
> +  template <typename U>
> +  struct B {};
> +};
> +
> +template <typename T>
> +struct C : public A<T>::template B<T> {
> +};
> +
> +template <typename T>
> +struct C2 : public A<int>::B<T> {
> +};
> diff --git a/gcc/testsuite/g++.dg/template/error4.C b/gcc/testsuite/g++.dg/template/error4.C
> index 9d76561aa02..a5030f06c98 100644
> --- a/gcc/testsuite/g++.dg/template/error4.C
> +++ b/gcc/testsuite/g++.dg/template/error4.C
> @@ -5,5 +5,4 @@ template<class T> struct C1
>   };
>   
>   template<class T, class U>
> -void foo(typename C1<T>::C2<U>::Type *) { } // { dg-error "template" "error " }
> -// { dg-message "note" "note" { target *-*-* } .-1 }
> +void foo(typename C1<T>::C2<U>::Type *) { }
> diff --git a/gcc/testsuite/g++.dg/template/meminit2.C b/gcc/testsuite/g++.dg/template/meminit2.C
> index db6e0427fc7..6a56a806746 100644
> --- a/gcc/testsuite/g++.dg/template/meminit2.C
> +++ b/gcc/testsuite/g++.dg/template/meminit2.C
> @@ -14,8 +14,6 @@ struct A : typename O<T>::template I<int> {   // { dg-error "keyword 'typename'
>   
>   template <typename T>
>   struct B : O<T>::template I<int> {
> -  B() :    O<T>::I<int>()   // { dg-error "used as template|it is a template" }
> +  B() :    O<T>::I<int>()
>     {}
>   };
> -
> -// { dg-bogus "end of input" "bogus token skipping in the parser" { xfail *-*-* } 17 }
> diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> index 5d386132436..60cd5e3f44e 100644
> --- a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> +++ b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> @@ -3,10 +3,8 @@
>   
>   template <class T>
>   struct S {
> -  typedef typename T::Y<T>::Z X; // { dg-error "non-template" "non-template" } No Y in A
> -// { dg-message "note" "note" { target *-*-* } .-1 }
> -// { dg-error "does not declare" "not declare" { target *-*-* } .-2 }
> -  X x; // { dg-error "does not name a type" } No Y in A
> +  typedef typename T::Y<T>::Z X; // { dg-error "not a class template" } No Y in A
> +  X x;
>   };
>   
>   struct A {
> 
> base-commit: 48817fbd7616f086ac7bb1dd38b862f78762c9b8
>
Li, Pan2 via Gcc-patches March 26, 2020, 7:44 p.m. UTC | #10
On Thu, Mar 26, 2020 at 01:43:28AM -0400, Jason Merrill wrote:
> > > I think you want to check all typedefs like in e.g. find_parameter_packs_r;
> > > if the name is a typedef, it's only suitable if
> > > alias_template_specialization_p.
> > 
> > ..this: Since alias_template_specialization_p already checks TYPE_P and
> > typedef_variant_p, can we simply use that?
> 
> But not all typedefs are aliases; 'typedef A<int> type;' does not make
> '::template type' valid just because '::template A<int>' would be.  If it's
> an alias template specialization, OK; if it's any other typedef, not OK.

This is actually already handled well, '::template type' is rejected but
'::template A<int>' works.  I've added alias-decl3.C to that effect but
otherwise there are no changes in this patch:

-- >8 --
Consider

  template <typename T> class A {
    template <typename U> class B {
      void fn(typename A<T>::B<U>);
    };
  };

which is rejected with
error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
whereas clang/icc/msvc accept it.

"typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
don't mention it anywhere, because the typename-specifier wasn't in C++11;
it was only added to the language in N1376.  Instead, we handle it as
an elaborated-type-specifier (not a problem thus far).   So we get to
cp_parser_nested_name_specifier_opt which has a loop that breaks if we
don't see a < or ::, but that means we can -- tentatively -- parse even
B<U> which is not a nested-name-specifier (it doesn't end with a ::).

I think this should compile because [temp.names]/4 says: "In a qualified-id
used as the name in a typename-specifier, elaborated-type-specifier,
using-declaration, or class-or-decltype, an optional keyword template
appearing at the top level is ignored.", added in DR 1710.  Also see
DR 1812.

This issue on its own is not a significant problem or a regression.
However, in C++20, the typename here becomes optional, and so this test
is rejected in C++20, but accepted in C++17:

  template <typename T> class A {
    template <typename U> class B {
      void fn(A<T>::B<U>);
    };
  };

Here we morph A<T>::B<U> into a typename-specifier, but that happens
in cp_parser_simple_type_specifier and we never handle it as above.
To fake the template keyword I'm afraid we need to use cp_parser_template_id
with template_keyword_p=true as in the patch below.  The tricky thing
is to avoid breaking concepts.

To handle DR 1710, I made cp_parser_nested_name_specifier_opt assume that
when we're naming a type, the template keyword is present, too.  That
revealed a bug: DR 1710 also says that the template keyword can be followed
by an alias template, but we weren't prepared to handle that.  alias-decl?.C
exercise this.

gcc/cp:

	DR 1710
	PR c++/94057 - template keyword in a typename-specifier.
	* parser.c (check_template_keyword_in_nested_name_spec): New.
	(cp_parser_nested_name_specifier_opt): Implement DR1710, optional
	'template'.  Call check_template_keyword_in_nested_name_spec.
	(cp_parser_simple_type_specifier): Assume that a <
	following a qualified-id in a typename-specifier begins
	a template argument list.

gcc/testsuite:

	DR 1710
	PR c++/94057 - template keyword in a typename-specifier.
	* g++.dg/cpp1y/alias-decl1.C: New test.
	* g++.dg/cpp1y/alias-decl2.C: New test.
	* g++.dg/cpp1y/alias-decl3.C: New test.
	* g++.dg/parse/missing-template1.C: Update dg-error.
	* g++.dg/parse/template3.C: Likewise.
	* g++.dg/template/error4.C: Likewise.
	* g++.dg/template/meminit2.C: Likewise.
	* g++.dg/template/dependent-name5.C: Likewise.
	* g++.dg/template/dependent-name7.C: New test.
	* g++.dg/template/dependent-name8.C: New test.
	* g++.dg/template/dependent-name9.C: New test.
	* g++.dg/template/dependent-name10.C: New test.
	* g++.dg/template/dependent-name11.C: New test.
	* g++.dg/template/dependent-name12.C: New test.
	* g++.dg/template/dependent-name13.C: New test.
	* g++.dg/template/dr1794.C: New test.
	* g++.dg/template/dr314.C: New test.
	* g++.dg/template/dr1710.C: New test.
	* g++.dg/template/dr1710-2.C: New test.
	* g++.old-deja/g++.pt/crash38.C: Update dg-error.
---
 gcc/cp/parser.c                               | 79 ++++++++++++++++---
 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C      |  9 +++
 gcc/testsuite/g++.dg/cpp1y/alias-decl2.C      |  8 ++
 gcc/testsuite/g++.dg/cpp1y/alias-decl3.C      |  9 +++
 .../g++.dg/parse/missing-template1.C          |  4 +-
 gcc/testsuite/g++.dg/parse/template3.C        |  5 +-
 .../g++.dg/template/dependent-name10.C        | 18 +++++
 .../g++.dg/template/dependent-name11.C        | 15 ++++
 .../g++.dg/template/dependent-name12.C        |  7 ++
 .../g++.dg/template/dependent-name13.C        |  8 ++
 .../g++.dg/template/dependent-name5.C         |  2 -
 .../g++.dg/template/dependent-name7.C         |  9 +++
 .../g++.dg/template/dependent-name8.C         |  9 +++
 .../g++.dg/template/dependent-name9.C         |  9 +++
 gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 +++
 gcc/testsuite/g++.dg/template/dr1710.C        |  9 +++
 gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++
 gcc/testsuite/g++.dg/template/dr314.C         | 15 ++++
 gcc/testsuite/g++.dg/template/error4.C        |  3 +-
 gcc/testsuite/g++.dg/template/meminit2.C      |  4 +-
 gcc/testsuite/g++.old-deja/g++.pt/crash38.C   |  6 +-
 21 files changed, 221 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl3.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name12.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name13.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
 create mode 100644 gcc/testsuite/g++.dg/template/dr314.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 05363653691..753b3148ab3 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -6266,6 +6266,32 @@ cp_parser_unqualified_id (cp_parser* parser,
     }
 }
 
+/* Check [temp.names]/5: A name prefixed by the keyword template shall
+   be a template-id or the name shall refer to a class template or an
+   alias template.  */
+
+static void
+check_template_keyword_in_nested_name_spec (tree name)
+{
+  if (CLASS_TYPE_P (name)
+      && ((CLASSTYPE_USE_TEMPLATE (name)
+	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
+	  || CLASSTYPE_IS_TEMPLATE (name)))
+    return;
+
+  if (TREE_CODE (name) == TYPENAME_TYPE
+      && TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
+    return;
+  /* Alias templates are also OK.  */
+  else if (alias_template_specialization_p (name, nt_opaque))
+    return;
+
+  permerror (input_location, TYPE_P (name)
+	     ? G_("%qT is not a template")
+	     : G_("%qD is not a template"),
+	     name);
+}
+
 /* Parse an (optional) nested-name-specifier.
 
    nested-name-specifier: [C++98]
@@ -6389,7 +6415,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
       /* Look for the optional `template' keyword, if this isn't the
 	 first time through the loop.  */
       if (success)
-	template_keyword_p = cp_parser_optional_template_keyword (parser);
+	{
+	  template_keyword_p = cp_parser_optional_template_keyword (parser);
+	  /* DR1710: "In a qualified-id used as the name in
+	     a typename-specifier, elaborated-type-specifier, using-declaration,
+	     or class-or-decltype, an optional keyword template appearing at
+	     the top level is ignored."  */
+	  if (!template_keyword_p
+	      && typename_keyword_p
+	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
+	    template_keyword_p = true;
+	}
 
       /* Save the old scope since the name lookup we are about to do
 	 might destroy it.  */
@@ -6580,18 +6616,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
       if (TREE_CODE (new_scope) == TYPE_DECL)
 	new_scope = TREE_TYPE (new_scope);
       /* Uses of "template" must be followed by actual templates.  */
-      if (template_keyword_p
-	  && !(CLASS_TYPE_P (new_scope)
-	       && ((CLASSTYPE_USE_TEMPLATE (new_scope)
-		    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope)))
-		   || CLASSTYPE_IS_TEMPLATE (new_scope)))
-	  && !(TREE_CODE (new_scope) == TYPENAME_TYPE
-	       && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope))
-		   == TEMPLATE_ID_EXPR)))
-	permerror (input_location, TYPE_P (new_scope)
-		   ? G_("%qT is not a template")
-		   : G_("%qD is not a template"),
-		   new_scope);
+      if (template_keyword_p)
+	check_template_keyword_in_nested_name_spec (new_scope);
       /* If it is a class scope, try to complete it; we are about to
 	 be looking up names inside the class.  */
       if (TYPE_P (new_scope)
@@ -18120,6 +18146,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 		}
 	    }
 	}
+      /* DR 1812: A < following a qualified-id in a typename-specifier
+	 could safely be assumed to begin a template argument list, so
+	 the template keyword should be optional.  */
+      else if (parser->scope
+	       && qualified_p
+	       && typename_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
+	{
+	  cp_parser_parse_tentatively (parser);
+
+	  type = cp_parser_template_id (parser,
+					/*template_keyword_p=*/true,
+					/*check_dependency_p=*/true,
+					none_type,
+					/*is_declaration=*/false);
+	  /* This is handled below, so back off.  */
+	  if (type && concept_check_p (type))
+	    cp_parser_simulate_error (parser);
+
+	  if (!cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
+	    type = make_typename_type (parser->scope, type, typename_type,
+				       /*complain=*/tf_error);
+	  else if (TREE_CODE (type) != TYPE_DECL)
+	    type = NULL_TREE;
+	}
 
       /* Otherwise, look for a type-name.  */
       if (!type)
diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
new file mode 100644
index 00000000000..e5397e71bd6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile { target c++14 } }
+
+template <int> struct S {
+  template <int> struct A;
+  template <int N> using U = typename A<N>::foo;
+};
+template <typename T> typename S<1>::U<T::foo>::type a;
+template <typename T> typename S<1>::template U<T::foo>::type a2;
diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
new file mode 100644
index 00000000000..f42b425a30b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
@@ -0,0 +1,8 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile { target c++14 } }
+
+template <typename T> struct S {
+  template <typename TT>
+  using U = TT;
+};
+template <typename T> typename S<int>::template U<T>::type foo;
diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl3.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl3.C
new file mode 100644
index 00000000000..b950b20b5d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl3.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile { target c++14 } }
+
+template <typename T> struct S {
+  template<typename TT> struct A { };
+  typedef A<int> type;
+};
+template <typename T> typename S<int>::template A<int> foo;
+template <typename T> typename S<int>::template type foo2; // { dg-error "expected template-id" }
diff --git a/gcc/testsuite/g++.dg/parse/missing-template1.C b/gcc/testsuite/g++.dg/parse/missing-template1.C
index fcc3aa0c3ff..19c1433d09f 100644
--- a/gcc/testsuite/g++.dg/parse/missing-template1.C
+++ b/gcc/testsuite/g++.dg/parse/missing-template1.C
@@ -12,9 +12,7 @@ template <typename T> struct A
 
 template <typename T> void foo()
 {
-    typedef typename A<T>::B<T>::X Y; // { dg-error "non-template" "non" }
-    // { dg-error "not declare" "decl" { target *-*-* } .-1 }
-    // { dg-message "note" "note" { target *-*-* } .-2 }
+    typedef typename A<T>::B<T>::X Y;
 }
 
 void bar()
diff --git a/gcc/testsuite/g++.dg/parse/template3.C b/gcc/testsuite/g++.dg/parse/template3.C
index c284a5ee040..8da8a48a3c3 100644
--- a/gcc/testsuite/g++.dg/parse/template3.C
+++ b/gcc/testsuite/g++.dg/parse/template3.C
@@ -13,7 +13,4 @@ struct X : Outer<b>::template Inner<T>
 {};
 
 template <bool b, typename T>
-struct Y : Outer<b>::Inner<T> {}; // { dg-error "used as template" "temp" }
-// { dg-error "expected" "exp" { target *-*-* } .-1 }
-// { dg-message "note" "note" { target *-*-* } .-2 }
-
+struct Y : Outer<b>::Inner<T> {};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
new file mode 100644
index 00000000000..18e024f7e6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
@@ -0,0 +1,18 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    typedef int TT;
+    typedef int TT2;
+    typedef int TT3;
+    typedef int TT4;
+  };
+};
+
+struct X : A<int>::B<int> {
+  using A<int>::template B<int>::TT;
+  using typename A<int>::template B<int>::TT2;
+  using A<int>::B<int>::TT3;
+  using typename A<int>::B<int>::TT4;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
new file mode 100644
index 00000000000..687a9bd5df5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
@@ -0,0 +1,15 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile { target c++11 } }
+
+template<typename T> struct A {
+  template<typename U>
+  struct W { };
+};
+
+void
+g ()
+{
+  // class-key nested-name-specifier template[opt] simple-template-id
+  struct A<int>::W<int> w;
+  struct A<int>::template W<int> w2;
+}
diff --git a/gcc/testsuite/g++.dg/template/dependent-name12.C b/gcc/testsuite/g++.dg/template/dependent-name12.C
new file mode 100644
index 00000000000..7ee94e7457d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name12.C
@@ -0,0 +1,7 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+
+template <bool> struct A;
+template <typename, typename> struct B;
+template <typename T, typename U, typename V> struct B<T U::*, V> {
+  typename A<V::x>::type::type t;
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name13.C b/gcc/testsuite/g++.dg/template/dependent-name13.C
new file mode 100644
index 00000000000..1e971168657
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name13.C
@@ -0,0 +1,8 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+
+template<typename T> struct S {
+  void fn(typename T::template B<int>::template C<int>);
+  void fn2(typename T::B<int>::template C<int>);
+  void fn3(typename T::template B<int>::C<int>);
+  void fn4(typename T::B<int>::C<int>);
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
index fc78983324b..15c1acb0347 100644
--- a/gcc/testsuite/g++.dg/template/dependent-name5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -22,9 +22,7 @@ struct A
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
new file mode 100644
index 00000000000..3dfa42d2df0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(A<T>::B<U>&);
+    void fn(A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
new file mode 100644
index 00000000000..ad9e44f9b85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::B<U>&);
+    void fn(typename A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
new file mode 100644
index 00000000000..6dfdbc176c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
@@ -0,0 +1,9 @@
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::template B<U>&);
+    void fn(typename A<T>::template B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
new file mode 100644
index 00000000000..99d49b746b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
@@ -0,0 +1,10 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+  };
+};
+
+template<typename T> struct D : A<int>::B<int> {};
+template<typename T> struct D2 : A<int>::template B<int> {};
diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
new file mode 100644
index 00000000000..c945977971f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1710.C
@@ -0,0 +1,9 @@
+// DR 1710 - Missing template keyword in class-or-decltype
+// { dg-do compile }
+
+template<typename T> struct D : T::template B<int>::template C<int> {};
+template<typename T> struct D2 : T::B<int>::template C<int> {};
+template<typename T> struct D3 : T::template B<int>::C<int> {};
+template<typename T> struct D4 : T::B<int>::C<int> {};
+template<typename T> struct D5 : T::template B<int>::type::type {};
+template<typename T> struct D6 : T::B<int>::type::type {};
diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
new file mode 100644
index 00000000000..f629d7d0b98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr1794.C
@@ -0,0 +1,14 @@
+// DR 1794 - template keyword and alias templates.
+// { dg-do compile { target c++11 } }
+
+template<template<typename> class Template>
+struct Internal {
+  template<typename Arg>
+  using Bind = Template<Arg>;
+};
+
+template<template<typename> class Template, typename Arg>
+using Instantiate = Template<Arg>;
+
+template<template<typename> class Template, typename Argument>
+using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
new file mode 100644
index 00000000000..7c0d8ac592f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dr314.C
@@ -0,0 +1,15 @@
+// DR 314 - template in base class specifier.
+
+template <typename T>
+struct A {
+  template <typename U>
+  struct B {};
+};
+
+template <typename T>
+struct C : public A<T>::template B<T> {
+};
+
+template <typename T>
+struct C2 : public A<int>::B<T> {
+};
diff --git a/gcc/testsuite/g++.dg/template/error4.C b/gcc/testsuite/g++.dg/template/error4.C
index 9d76561aa02..a5030f06c98 100644
--- a/gcc/testsuite/g++.dg/template/error4.C
+++ b/gcc/testsuite/g++.dg/template/error4.C
@@ -5,5 +5,4 @@ template<class T> struct C1
 };
 
 template<class T, class U>
-void foo(typename C1<T>::C2<U>::Type *) { } // { dg-error "template" "error " }
-// { dg-message "note" "note" { target *-*-* } .-1 }
+void foo(typename C1<T>::C2<U>::Type *) { }
diff --git a/gcc/testsuite/g++.dg/template/meminit2.C b/gcc/testsuite/g++.dg/template/meminit2.C
index db6e0427fc7..6a56a806746 100644
--- a/gcc/testsuite/g++.dg/template/meminit2.C
+++ b/gcc/testsuite/g++.dg/template/meminit2.C
@@ -14,8 +14,6 @@ struct A : typename O<T>::template I<int> {   // { dg-error "keyword 'typename'
 
 template <typename T> 
 struct B : O<T>::template I<int> {
-  B() :    O<T>::I<int>()   // { dg-error "used as template|it is a template" }
+  B() :    O<T>::I<int>()
   {}
 };
-
-// { dg-bogus "end of input" "bogus token skipping in the parser" { xfail *-*-* } 17 }
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
index 5d386132436..60cd5e3f44e 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
@@ -3,10 +3,8 @@
 
 template <class T>
 struct S {
-  typedef typename T::Y<T>::Z X; // { dg-error "non-template" "non-template" } No Y in A
-// { dg-message "note" "note" { target *-*-* } .-1 }
-// { dg-error "does not declare" "not declare" { target *-*-* } .-2 }
-  X x; // { dg-error "does not name a type" } No Y in A
+  typedef typename T::Y<T>::Z X; // { dg-error "not a class template" } No Y in A
+  X x;
 };
 
 struct A {

base-commit: 16948c54b7576fb4b27c59915eac71a0c6bf94f6
Li, Pan2 via Gcc-patches March 26, 2020, 10:01 p.m. UTC | #11
On 3/26/20 3:44 PM, Marek Polacek wrote:
> On Thu, Mar 26, 2020 at 01:43:28AM -0400, Jason Merrill wrote:
>>>> I think you want to check all typedefs like in e.g. find_parameter_packs_r;
>>>> if the name is a typedef, it's only suitable if
>>>> alias_template_specialization_p.
>>>
>>> ..this: Since alias_template_specialization_p already checks TYPE_P and
>>> typedef_variant_p, can we simply use that?
>>
>> But not all typedefs are aliases; 'typedef A<int> type;' does not make
>> '::template type' valid just because '::template A<int>' would be.  If it's
>> an alias template specialization, OK; if it's any other typedef, not OK.
> 
> This is actually already handled well, '::template type' is rejected but
> '::template A<int>' works.  I've added alias-decl3.C to that effect but
> otherwise there are no changes in this patch:

Then the patch is OK, thanks.

> -- >8 --
> Consider
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(typename A<T>::B<U>);
>      };
>    };
> 
> which is rejected with
> error: 'typename A<T>::B' names 'template<class T> template<class U> class A<T>::B', which is not a type
> whereas clang/icc/msvc accept it.
> 
> "typename A<T>::B<U>" is a typename-specifier.  Sadly, our comments
> don't mention it anywhere, because the typename-specifier wasn't in C++11;
> it was only added to the language in N1376.  Instead, we handle it as
> an elaborated-type-specifier (not a problem thus far).   So we get to
> cp_parser_nested_name_specifier_opt which has a loop that breaks if we
> don't see a < or ::, but that means we can -- tentatively -- parse even
> B<U> which is not a nested-name-specifier (it doesn't end with a ::).
> 
> I think this should compile because [temp.names]/4 says: "In a qualified-id
> used as the name in a typename-specifier, elaborated-type-specifier,
> using-declaration, or class-or-decltype, an optional keyword template
> appearing at the top level is ignored.", added in DR 1710.  Also see
> DR 1812.
> 
> This issue on its own is not a significant problem or a regression.
> However, in C++20, the typename here becomes optional, and so this test
> is rejected in C++20, but accepted in C++17:
> 
>    template <typename T> class A {
>      template <typename U> class B {
>        void fn(A<T>::B<U>);
>      };
>    };
> 
> Here we morph A<T>::B<U> into a typename-specifier, but that happens
> in cp_parser_simple_type_specifier and we never handle it as above.
> To fake the template keyword I'm afraid we need to use cp_parser_template_id
> with template_keyword_p=true as in the patch below.  The tricky thing
> is to avoid breaking concepts.
> 
> To handle DR 1710, I made cp_parser_nested_name_specifier_opt assume that
> when we're naming a type, the template keyword is present, too.  That
> revealed a bug: DR 1710 also says that the template keyword can be followed
> by an alias template, but we weren't prepared to handle that.  alias-decl?.C
> exercise this.
> 
> gcc/cp:
> 
> 	DR 1710
> 	PR c++/94057 - template keyword in a typename-specifier.
> 	* parser.c (check_template_keyword_in_nested_name_spec): New.
> 	(cp_parser_nested_name_specifier_opt): Implement DR1710, optional
> 	'template'.  Call check_template_keyword_in_nested_name_spec.
> 	(cp_parser_simple_type_specifier): Assume that a <
> 	following a qualified-id in a typename-specifier begins
> 	a template argument list.
> 
> gcc/testsuite:
> 
> 	DR 1710
> 	PR c++/94057 - template keyword in a typename-specifier.
> 	* g++.dg/cpp1y/alias-decl1.C: New test.
> 	* g++.dg/cpp1y/alias-decl2.C: New test.
> 	* g++.dg/cpp1y/alias-decl3.C: New test.
> 	* g++.dg/parse/missing-template1.C: Update dg-error.
> 	* g++.dg/parse/template3.C: Likewise.
> 	* g++.dg/template/error4.C: Likewise.
> 	* g++.dg/template/meminit2.C: Likewise.
> 	* g++.dg/template/dependent-name5.C: Likewise.
> 	* g++.dg/template/dependent-name7.C: New test.
> 	* g++.dg/template/dependent-name8.C: New test.
> 	* g++.dg/template/dependent-name9.C: New test.
> 	* g++.dg/template/dependent-name10.C: New test.
> 	* g++.dg/template/dependent-name11.C: New test.
> 	* g++.dg/template/dependent-name12.C: New test.
> 	* g++.dg/template/dependent-name13.C: New test.
> 	* g++.dg/template/dr1794.C: New test.
> 	* g++.dg/template/dr314.C: New test.
> 	* g++.dg/template/dr1710.C: New test.
> 	* g++.dg/template/dr1710-2.C: New test.
> 	* g++.old-deja/g++.pt/crash38.C: Update dg-error.
> ---
>   gcc/cp/parser.c                               | 79 ++++++++++++++++---
>   gcc/testsuite/g++.dg/cpp1y/alias-decl1.C      |  9 +++
>   gcc/testsuite/g++.dg/cpp1y/alias-decl2.C      |  8 ++
>   gcc/testsuite/g++.dg/cpp1y/alias-decl3.C      |  9 +++
>   .../g++.dg/parse/missing-template1.C          |  4 +-
>   gcc/testsuite/g++.dg/parse/template3.C        |  5 +-
>   .../g++.dg/template/dependent-name10.C        | 18 +++++
>   .../g++.dg/template/dependent-name11.C        | 15 ++++
>   .../g++.dg/template/dependent-name12.C        |  7 ++
>   .../g++.dg/template/dependent-name13.C        |  8 ++
>   .../g++.dg/template/dependent-name5.C         |  2 -
>   .../g++.dg/template/dependent-name7.C         |  9 +++
>   .../g++.dg/template/dependent-name8.C         |  9 +++
>   .../g++.dg/template/dependent-name9.C         |  9 +++
>   gcc/testsuite/g++.dg/template/dr1710-2.C      | 10 +++
>   gcc/testsuite/g++.dg/template/dr1710.C        |  9 +++
>   gcc/testsuite/g++.dg/template/dr1794.C        | 14 ++++
>   gcc/testsuite/g++.dg/template/dr314.C         | 15 ++++
>   gcc/testsuite/g++.dg/template/error4.C        |  3 +-
>   gcc/testsuite/g++.dg/template/meminit2.C      |  4 +-
>   gcc/testsuite/g++.old-deja/g++.pt/crash38.C   |  6 +-
>   21 files changed, 221 insertions(+), 31 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/alias-decl3.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name10.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name11.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name12.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name13.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name7.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name8.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dependent-name9.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710-2.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1710.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr1794.C
>   create mode 100644 gcc/testsuite/g++.dg/template/dr314.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 05363653691..753b3148ab3 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -6266,6 +6266,32 @@ cp_parser_unqualified_id (cp_parser* parser,
>       }
>   }
>   
> +/* Check [temp.names]/5: A name prefixed by the keyword template shall
> +   be a template-id or the name shall refer to a class template or an
> +   alias template.  */
> +
> +static void
> +check_template_keyword_in_nested_name_spec (tree name)
> +{
> +  if (CLASS_TYPE_P (name)
> +      && ((CLASSTYPE_USE_TEMPLATE (name)
> +	   && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
> +	  || CLASSTYPE_IS_TEMPLATE (name)))
> +    return;
> +
> +  if (TREE_CODE (name) == TYPENAME_TYPE
> +      && TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
> +    return;
> +  /* Alias templates are also OK.  */
> +  else if (alias_template_specialization_p (name, nt_opaque))
> +    return;
> +
> +  permerror (input_location, TYPE_P (name)
> +	     ? G_("%qT is not a template")
> +	     : G_("%qD is not a template"),
> +	     name);
> +}
> +
>   /* Parse an (optional) nested-name-specifier.
>   
>      nested-name-specifier: [C++98]
> @@ -6389,7 +6415,17 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>         /* Look for the optional `template' keyword, if this isn't the
>   	 first time through the loop.  */
>         if (success)
> -	template_keyword_p = cp_parser_optional_template_keyword (parser);
> +	{
> +	  template_keyword_p = cp_parser_optional_template_keyword (parser);
> +	  /* DR1710: "In a qualified-id used as the name in
> +	     a typename-specifier, elaborated-type-specifier, using-declaration,
> +	     or class-or-decltype, an optional keyword template appearing at
> +	     the top level is ignored."  */
> +	  if (!template_keyword_p
> +	      && typename_keyword_p
> +	      && cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
> +	    template_keyword_p = true;
> +	}
>   
>         /* Save the old scope since the name lookup we are about to do
>   	 might destroy it.  */
> @@ -6580,18 +6616,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>         if (TREE_CODE (new_scope) == TYPE_DECL)
>   	new_scope = TREE_TYPE (new_scope);
>         /* Uses of "template" must be followed by actual templates.  */
> -      if (template_keyword_p
> -	  && !(CLASS_TYPE_P (new_scope)
> -	       && ((CLASSTYPE_USE_TEMPLATE (new_scope)
> -		    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope)))
> -		   || CLASSTYPE_IS_TEMPLATE (new_scope)))
> -	  && !(TREE_CODE (new_scope) == TYPENAME_TYPE
> -	       && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope))
> -		   == TEMPLATE_ID_EXPR)))
> -	permerror (input_location, TYPE_P (new_scope)
> -		   ? G_("%qT is not a template")
> -		   : G_("%qD is not a template"),
> -		   new_scope);
> +      if (template_keyword_p)
> +	check_template_keyword_in_nested_name_spec (new_scope);
>         /* If it is a class scope, try to complete it; we are about to
>   	 be looking up names inside the class.  */
>         if (TYPE_P (new_scope)
> @@ -18120,6 +18146,33 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>   		}
>   	    }
>   	}
> +      /* DR 1812: A < following a qualified-id in a typename-specifier
> +	 could safely be assumed to begin a template argument list, so
> +	 the template keyword should be optional.  */
> +      else if (parser->scope
> +	       && qualified_p
> +	       && typename_p
> +	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
> +	{
> +	  cp_parser_parse_tentatively (parser);
> +
> +	  type = cp_parser_template_id (parser,
> +					/*template_keyword_p=*/true,
> +					/*check_dependency_p=*/true,
> +					none_type,
> +					/*is_declaration=*/false);
> +	  /* This is handled below, so back off.  */
> +	  if (type && concept_check_p (type))
> +	    cp_parser_simulate_error (parser);
> +
> +	  if (!cp_parser_parse_definitely (parser))
> +	    type = NULL_TREE;
> +	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
> +	    type = make_typename_type (parser->scope, type, typename_type,
> +				       /*complain=*/tf_error);
> +	  else if (TREE_CODE (type) != TYPE_DECL)
> +	    type = NULL_TREE;
> +	}
>   
>         /* Otherwise, look for a type-name.  */
>         if (!type)
> diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
> new file mode 100644
> index 00000000000..e5397e71bd6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl1.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile { target c++14 } }
> +
> +template <int> struct S {
> +  template <int> struct A;
> +  template <int N> using U = typename A<N>::foo;
> +};
> +template <typename T> typename S<1>::U<T::foo>::type a;
> +template <typename T> typename S<1>::template U<T::foo>::type a2;
> diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
> new file mode 100644
> index 00000000000..f42b425a30b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl2.C
> @@ -0,0 +1,8 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile { target c++14 } }
> +
> +template <typename T> struct S {
> +  template <typename TT>
> +  using U = TT;
> +};
> +template <typename T> typename S<int>::template U<T>::type foo;
> diff --git a/gcc/testsuite/g++.dg/cpp1y/alias-decl3.C b/gcc/testsuite/g++.dg/cpp1y/alias-decl3.C
> new file mode 100644
> index 00000000000..b950b20b5d8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/alias-decl3.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile { target c++14 } }
> +
> +template <typename T> struct S {
> +  template<typename TT> struct A { };
> +  typedef A<int> type;
> +};
> +template <typename T> typename S<int>::template A<int> foo;
> +template <typename T> typename S<int>::template type foo2; // { dg-error "expected template-id" }
> diff --git a/gcc/testsuite/g++.dg/parse/missing-template1.C b/gcc/testsuite/g++.dg/parse/missing-template1.C
> index fcc3aa0c3ff..19c1433d09f 100644
> --- a/gcc/testsuite/g++.dg/parse/missing-template1.C
> +++ b/gcc/testsuite/g++.dg/parse/missing-template1.C
> @@ -12,9 +12,7 @@ template <typename T> struct A
>   
>   template <typename T> void foo()
>   {
> -    typedef typename A<T>::B<T>::X Y; // { dg-error "non-template" "non" }
> -    // { dg-error "not declare" "decl" { target *-*-* } .-1 }
> -    // { dg-message "note" "note" { target *-*-* } .-2 }
> +    typedef typename A<T>::B<T>::X Y;
>   }
>   
>   void bar()
> diff --git a/gcc/testsuite/g++.dg/parse/template3.C b/gcc/testsuite/g++.dg/parse/template3.C
> index c284a5ee040..8da8a48a3c3 100644
> --- a/gcc/testsuite/g++.dg/parse/template3.C
> +++ b/gcc/testsuite/g++.dg/parse/template3.C
> @@ -13,7 +13,4 @@ struct X : Outer<b>::template Inner<T>
>   {};
>   
>   template <bool b, typename T>
> -struct Y : Outer<b>::Inner<T> {}; // { dg-error "used as template" "temp" }
> -// { dg-error "expected" "exp" { target *-*-* } .-1 }
> -// { dg-message "note" "note" { target *-*-* } .-2 }
> -
> +struct Y : Outer<b>::Inner<T> {};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name10.C b/gcc/testsuite/g++.dg/template/dependent-name10.C
> new file mode 100644
> index 00000000000..18e024f7e6d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name10.C
> @@ -0,0 +1,18 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    typedef int TT;
> +    typedef int TT2;
> +    typedef int TT3;
> +    typedef int TT4;
> +  };
> +};
> +
> +struct X : A<int>::B<int> {
> +  using A<int>::template B<int>::TT;
> +  using typename A<int>::template B<int>::TT2;
> +  using A<int>::B<int>::TT3;
> +  using typename A<int>::B<int>::TT4;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name11.C b/gcc/testsuite/g++.dg/template/dependent-name11.C
> new file mode 100644
> index 00000000000..687a9bd5df5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name11.C
> @@ -0,0 +1,15 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T> struct A {
> +  template<typename U>
> +  struct W { };
> +};
> +
> +void
> +g ()
> +{
> +  // class-key nested-name-specifier template[opt] simple-template-id
> +  struct A<int>::W<int> w;
> +  struct A<int>::template W<int> w2;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name12.C b/gcc/testsuite/g++.dg/template/dependent-name12.C
> new file mode 100644
> index 00000000000..7ee94e7457d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name12.C
> @@ -0,0 +1,7 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +
> +template <bool> struct A;
> +template <typename, typename> struct B;
> +template <typename T, typename U, typename V> struct B<T U::*, V> {
> +  typename A<V::x>::type::type t;
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name13.C b/gcc/testsuite/g++.dg/template/dependent-name13.C
> new file mode 100644
> index 00000000000..1e971168657
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name13.C
> @@ -0,0 +1,8 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +
> +template<typename T> struct S {
> +  void fn(typename T::template B<int>::template C<int>);
> +  void fn2(typename T::B<int>::template C<int>);
> +  void fn3(typename T::template B<int>::C<int>);
> +  void fn4(typename T::B<int>::C<int>);
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
> index fc78983324b..15c1acb0347 100644
> --- a/gcc/testsuite/g++.dg/template/dependent-name5.C
> +++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
> @@ -22,9 +22,7 @@ struct A
>   
>     typedef N<int>       type6;
>     typedef A::N<int>    type7;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T>::N<int> type8;
> -// { dg-error "" "" { target c++2a } .-1 }
>     typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
>     typedef typename A<T*>::template N<int> type10;
>   
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
> new file mode 100644
> index 00000000000..3dfa42d2df0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(A<T>::B<U>&);
> +    void fn(A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
> new file mode 100644
> index 00000000000..ad9e44f9b85
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::B<U>&);
> +    void fn(typename A<T>::B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
> new file mode 100644
> index 00000000000..6dfdbc176c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
> @@ -0,0 +1,9 @@
> +// PR c++/94057 - template keyword in a typename-specifier.
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +    B(typename A<T>::template B<U>&);
> +    void fn(typename A<T>::template B<U>);
> +  };
> +};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710-2.C b/gcc/testsuite/g++.dg/template/dr1710-2.C
> new file mode 100644
> index 00000000000..99d49b746b2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710-2.C
> @@ -0,0 +1,10 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct A {
> +  template<typename U> struct B {
> +  };
> +};
> +
> +template<typename T> struct D : A<int>::B<int> {};
> +template<typename T> struct D2 : A<int>::template B<int> {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1710.C b/gcc/testsuite/g++.dg/template/dr1710.C
> new file mode 100644
> index 00000000000..c945977971f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1710.C
> @@ -0,0 +1,9 @@
> +// DR 1710 - Missing template keyword in class-or-decltype
> +// { dg-do compile }
> +
> +template<typename T> struct D : T::template B<int>::template C<int> {};
> +template<typename T> struct D2 : T::B<int>::template C<int> {};
> +template<typename T> struct D3 : T::template B<int>::C<int> {};
> +template<typename T> struct D4 : T::B<int>::C<int> {};
> +template<typename T> struct D5 : T::template B<int>::type::type {};
> +template<typename T> struct D6 : T::B<int>::type::type {};
> diff --git a/gcc/testsuite/g++.dg/template/dr1794.C b/gcc/testsuite/g++.dg/template/dr1794.C
> new file mode 100644
> index 00000000000..f629d7d0b98
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr1794.C
> @@ -0,0 +1,14 @@
> +// DR 1794 - template keyword and alias templates.
> +// { dg-do compile { target c++11 } }
> +
> +template<template<typename> class Template>
> +struct Internal {
> +  template<typename Arg>
> +  using Bind = Template<Arg>;
> +};
> +
> +template<template<typename> class Template, typename Arg>
> +using Instantiate = Template<Arg>;
> +
> +template<template<typename> class Template, typename Argument>
> +using Bind = Instantiate<Internal<Template>::template Bind, Argument>;
> diff --git a/gcc/testsuite/g++.dg/template/dr314.C b/gcc/testsuite/g++.dg/template/dr314.C
> new file mode 100644
> index 00000000000..7c0d8ac592f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/dr314.C
> @@ -0,0 +1,15 @@
> +// DR 314 - template in base class specifier.
> +
> +template <typename T>
> +struct A {
> +  template <typename U>
> +  struct B {};
> +};
> +
> +template <typename T>
> +struct C : public A<T>::template B<T> {
> +};
> +
> +template <typename T>
> +struct C2 : public A<int>::B<T> {
> +};
> diff --git a/gcc/testsuite/g++.dg/template/error4.C b/gcc/testsuite/g++.dg/template/error4.C
> index 9d76561aa02..a5030f06c98 100644
> --- a/gcc/testsuite/g++.dg/template/error4.C
> +++ b/gcc/testsuite/g++.dg/template/error4.C
> @@ -5,5 +5,4 @@ template<class T> struct C1
>   };
>   
>   template<class T, class U>
> -void foo(typename C1<T>::C2<U>::Type *) { } // { dg-error "template" "error " }
> -// { dg-message "note" "note" { target *-*-* } .-1 }
> +void foo(typename C1<T>::C2<U>::Type *) { }
> diff --git a/gcc/testsuite/g++.dg/template/meminit2.C b/gcc/testsuite/g++.dg/template/meminit2.C
> index db6e0427fc7..6a56a806746 100644
> --- a/gcc/testsuite/g++.dg/template/meminit2.C
> +++ b/gcc/testsuite/g++.dg/template/meminit2.C
> @@ -14,8 +14,6 @@ struct A : typename O<T>::template I<int> {   // { dg-error "keyword 'typename'
>   
>   template <typename T>
>   struct B : O<T>::template I<int> {
> -  B() :    O<T>::I<int>()   // { dg-error "used as template|it is a template" }
> +  B() :    O<T>::I<int>()
>     {}
>   };
> -
> -// { dg-bogus "end of input" "bogus token skipping in the parser" { xfail *-*-* } 17 }
> diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> index 5d386132436..60cd5e3f44e 100644
> --- a/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> +++ b/gcc/testsuite/g++.old-deja/g++.pt/crash38.C
> @@ -3,10 +3,8 @@
>   
>   template <class T>
>   struct S {
> -  typedef typename T::Y<T>::Z X; // { dg-error "non-template" "non-template" } No Y in A
> -// { dg-message "note" "note" { target *-*-* } .-1 }
> -// { dg-error "does not declare" "not declare" { target *-*-* } .-2 }
> -  X x; // { dg-error "does not name a type" } No Y in A
> +  typedef typename T::Y<T>::Z X; // { dg-error "not a class template" } No Y in A
> +  X x;
>   };
>   
>   struct A {
> 
> base-commit: 16948c54b7576fb4b27c59915eac71a0c6bf94f6
>
diff mbox series

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..f4175955992 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18113,6 +18113,33 @@  cp_parser_simple_type_specifier (cp_parser* parser,
 		}
 	    }
 	}
+      /* DR 1812: A < following a qualified-id in a typename-specifier
+	 could safely be assumed to begin a template argument list, so
+	 the template keyword should be optional.  */
+      else if (parser->scope
+	       && qualified_p
+	       && typename_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID))
+	{
+	  cp_parser_parse_tentatively (parser);
+
+	  type = cp_parser_template_id (parser,
+					/*template_keyword_p=*/true,
+					/*check_dependency_p=*/true,
+					none_type,
+					/*is_declaration=*/false);
+	  /* This is handled below, so back off.  */
+	  if (type && concept_check_p (type))
+	    cp_parser_simulate_error (parser);
+
+	  if (!cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	  else if (TREE_CODE (type) == TEMPLATE_ID_EXPR)
+	    type = make_typename_type (parser->scope, type, typename_type,
+				       /*complain=*/tf_error);
+	  else if (TREE_CODE (type) != TYPE_DECL)
+	    type = NULL_TREE;
+	}
 
       /* Otherwise, look for a type-name.  */
       if (!type)
@@ -23636,8 +23663,9 @@  cp_parser_class_name (cp_parser *parser,
       && decl != error_mark_node
       && !is_overloaded_fn (decl))
     {
-      decl = make_typename_type (scope, decl, typename_type,
-				 /*complain=*/tf_error);
+      tsubst_flags_t complain = (cp_parser_parsing_tentatively (parser)
+				 ? tf_none : tf_error);
+      decl = make_typename_type (scope, decl, typename_type, complain);
       if (decl != error_mark_node)
 	decl = TYPE_NAME (decl);
     }
diff --git a/gcc/testsuite/g++.dg/template/dependent-name5.C b/gcc/testsuite/g++.dg/template/dependent-name5.C
index fc78983324b..15c1acb0347 100644
--- a/gcc/testsuite/g++.dg/template/dependent-name5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-name5.C
@@ -22,9 +22,7 @@  struct A
 
   typedef N<int>       type6;
   typedef A::N<int>    type7;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T>::N<int> type8;
-// { dg-error "" "" { target c++2a } .-1 }
   typedef A<T*>::template N<int> type9;  // { dg-error "" "" { target c++17_down } }
   typedef typename A<T*>::template N<int> type10;
 
diff --git a/gcc/testsuite/g++.dg/template/dependent-name7.C b/gcc/testsuite/g++.dg/template/dependent-name7.C
new file mode 100644
index 00000000000..3dfa42d2df0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name7.C
@@ -0,0 +1,9 @@ 
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(A<T>::B<U>&);
+    void fn(A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name8.C b/gcc/testsuite/g++.dg/template/dependent-name8.C
new file mode 100644
index 00000000000..ad9e44f9b85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name8.C
@@ -0,0 +1,9 @@ 
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::B<U>&);
+    void fn(typename A<T>::B<U>);
+  };
+};
diff --git a/gcc/testsuite/g++.dg/template/dependent-name9.C b/gcc/testsuite/g++.dg/template/dependent-name9.C
new file mode 100644
index 00000000000..6dfdbc176c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-name9.C
@@ -0,0 +1,9 @@ 
+// PR c++/94057 - template keyword in a typename-specifier.
+// { dg-do compile }
+
+template<typename T> struct A {
+  template<typename U> struct B {
+    B(typename A<T>::template B<U>&);
+    void fn(typename A<T>::template B<U>);
+  };
+};