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


Reply via email to