https://gcc.gnu.org/g:2ca183c113effe2d334b2227f4fdfa5666db8874

commit r16-2146-g2ca183c113effe2d334b2227f4fdfa5666db8874
Author: Marek Polacek <pola...@redhat.com>
Date:   Tue Jul 8 14:36:37 2025 -0400

    c++: optional template after :: causing error [PR119838]
    
    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.
    
    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.
    
    Reviewed-by: Jason Merrill <ja...@redhat.com>

Diff:
---
 gcc/cp/parser.cc                        | 35 ++++++++++++++++++++++-----------
 gcc/testsuite/g++.dg/parse/template32.C | 13 ++++++++++++
 2 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 8148495f9887..968c0f50d162 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,22 @@ 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.
+   TODO: This function doesn't handle the C++14 change to make `::'
+   a nested-name-specifier by itself.  If it did, GLOBAL_P could probably
+   go.
 
    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 +7266,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 +7315,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 +7365,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 +21176,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 000000000000..b090f4074e38
--- /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;
+}

Reply via email to