On 7/8/25 4:22 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
Found while working on Reflection where we currently reject:

   constexpr auto r = ^^::template C<int>::type;

which should work, because "::template C<int>::" should match the

   nested-name-specifier template(opt) simple-template-id ::

production where the template is optional.  This bug is not limited
to Reflection as demonstrated by the attached test case, so I'm
submitting it separately.

Part of the problem is that cp_parser_nested_name_specifier_opt doesn't reflect the C++14 change to make :: a nested-name-specifier by itself. But that's a much larger change, and this patch seems like a fine workaround. But please add a comment about the C++14 change to the function comment; OK with that tweak.

The check_template_keyword_in_nested_name_spec call should ensure that
we're dealing with a template-id if we've seen "template".

        PR c++/119838

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_nested_name_specifier_opt): New global_p
        parameter.  Look for "template" when global_p is true.
        (cp_parser_simple_type_specifier): Pass global_p to
        cp_parser_nested_name_specifier_opt.

gcc/testsuite/ChangeLog:

        * g++.dg/parse/template32.C: New test.
---
  gcc/cp/parser.cc                        | 32 +++++++++++++++----------
  gcc/testsuite/g++.dg/parse/template32.C | 13 ++++++++++
  2 files changed, 33 insertions(+), 12 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/parse/template32.C

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 32c6a42b31d..70c670a6f1c 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2519,7 +2519,7 @@ static cp_expr cp_parser_id_expression
  static cp_expr cp_parser_unqualified_id
    (cp_parser *, bool, bool, bool, bool);
  static tree cp_parser_nested_name_specifier_opt
-  (cp_parser *, bool, bool, bool, bool, bool = false);
+  (cp_parser *, bool, bool, bool, bool, bool = false, bool = false);
  static tree cp_parser_nested_name_specifier
    (cp_parser *, bool, bool, bool, bool);
  static tree cp_parser_qualifying_entity
@@ -7242,18 +7242,19 @@ check_template_keyword_in_nested_name_spec (tree name)
       nested-name-specifier template [opt] simple-template-id ::
PARSER->SCOPE should be set appropriately before this function is
-   called.  TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in
-   effect.  TYPE_P is TRUE if we non-type bindings should be ignored
-   in name lookups.
+   called.  TYPENAME_KEYWORD_P is true if the `typename' keyword is in
+   effect.  TYPE_P is true if we non-type bindings should be ignored
+   in name lookups.  TEMPLATE_KEYWORD_P is true if the `template' keyword
+   was seen.  GLOBAL_P is true if `::' has already been parsed.
Sets PARSER->SCOPE to the class (TYPE) or namespace
     (NAMESPACE_DECL) specified by the nested-name-specifier, or leaves
     it unchanged if there is no nested-name-specifier.  Returns the new
     scope iff there is a nested-name-specifier, or NULL_TREE otherwise.
- If CHECK_DEPENDENCY_P is FALSE, names are looked up in dependent scopes.
+   If CHECK_DEPENDENCY_P is false, names are looked up in dependent scopes.
- If IS_DECLARATION is TRUE, the nested-name-specifier is known to be
+   If IS_DECLARATION is true, the nested-name-specifier is known to be
     part of a declaration and/or decl-specifier.  */
static tree
@@ -7262,7 +7263,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
                                     bool check_dependency_p,
                                     bool type_p,
                                     bool is_declaration,
-                                    bool template_keyword_p /* = false */)
+                                    bool template_keyword_p /* = false */,
+                                    bool global_p /* = false */)
  {
    bool success = false;
    cp_token_position start = 0;
@@ -7310,8 +7312,9 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
/* Spot cases that cannot be the beginning of a
         nested-name-specifier.  On the second and subsequent times
-        through the loop, we look for the `template' keyword.  */
-      if (success && token->keyword == RID_TEMPLATE)
+        (or the first, if '::' has already been parsed) through the
+        loop, we look for the `template' keyword.  */
+      if ((success || global_p) && token->keyword == RID_TEMPLATE)
        ;
        /* A template-id can start a nested-name-specifier.  */
        else if (token->type == CPP_TEMPLATE_ID)
@@ -7359,8 +7362,11 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
        cp_parser_parse_tentatively (parser);
/* Look for the optional `template' keyword, if this isn't the
-        first time through the loop.  */
-      if (success)
+        first time through the loop, or if we've already parsed '::';
+        this is then the
+          nested-name-specifier template [opt] simple-template-id ::
+        production.  */
+      if (success || global_p)
        {
          template_keyword_p = cp_parser_optional_template_keyword (parser);
          /* DR1710: "In a qualified-id used as the name in
@@ -21167,7 +21173,9 @@ cp_parser_simple_type_specifier (cp_parser* parser,
                                                /*typename_keyword_p=*/false,
                                                /*check_dependency_p=*/true,
                                                /*type_p=*/false,
-                                               /*is_declaration=*/false)
+                                               /*is_declaration=*/false,
+                                               /*template_keyword_p=*/false,
+                                               global_p)
           != NULL_TREE);
        /* If we have seen a nested-name-specifier, and the next token
         is `template', then we are using the template-id production.  */
diff --git a/gcc/testsuite/g++.dg/parse/template32.C 
b/gcc/testsuite/g++.dg/parse/template32.C
new file mode 100644
index 00000000000..b090f4074e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/template32.C
@@ -0,0 +1,13 @@
+// PR c++/119838
+// { dg-do compile { target c++11 } }
+
+template<typename T>
+struct S { using U = T; static const int x = 0; };
+void
+g ()
+{
+  ::S<int>::U a;
+  ::template S<int>::U b;
+  auto c = ::S<int>::x;
+  auto d = ::template S<int>::x;
+}

base-commit: 57da36bed1004d2b78057568176b76cb0a50d149

Reply via email to