]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: optional template after :: causing error [PR119838]
authorMarek Polacek <polacek@redhat.com>
Tue, 8 Jul 2025 18:36:37 +0000 (14:36 -0400)
committerMarek Polacek <polacek@redhat.com>
Wed, 9 Jul 2025 15:48:28 +0000 (11:48 -0400)
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 <jason@redhat.com>
gcc/cp/parser.cc
gcc/testsuite/g++.dg/parse/template32.C [new file with mode: 0644]

index 8148495f9887ac118ff8b80dc4b10867258b860c..968c0f50d1626b4b53e33a0626fcabf10456ad2c 100644 (file)
@@ -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 (file)
index 0000000..b090f40
--- /dev/null
@@ -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;
+}