On 3/14/20 4:06 AM, Jakub Jelinek wrote:
Hi!

The testcase shows some accepts-invalid (the ones without alignas) and
ice-on-invalid-code (the ones with alignas) cases.
If the enum doesn't have an underlying type and is not a definition,
the caller retries to parse it as elaborated type specifier.
E.g. for enum struct S s it will then pedwarn that elaborated type specifier
shouldn't have the struct/class keywords.
The problem is if the enum specifier is not followed by { when it has
underlying type.  In that case we have already called
cp_parser_parse_definitely to end the tentative parsing started at the
beginning of cp_parser_enum_specifier.  But the
cp_parser_error (parser, "expected %<;%> or %<{%>");
doesn't emit any error because the whole function is called from yet another
tentative parse and the caller starts parsing the elaborated type
specifier where the cp_parser_enum_specifier stopped (i.e. after the
underlying type token(s)).  The ultimate caller than commits the tentative
parsing (and even if it wouldn't, it wouldn't know what kind of error
to report).  I think after seeing enum {,struct,class} : type not being
followed by { or ;, there is no reason not to report it right away, as it
can't be valid C++, which is what the patch does.  Not sure if we shouldn't
also return error_mark_node instead of NULL_TREE, so that the caller doesn't
try to parse it as elaborated type specifier (the patch doesn't do that
right now).

Furthermore, while reading the code, I've noticed that
parser->colon_corrects_to_scope_p is saved and set to false at the start
of the function, but not restored back in some cases.  Don't have a testcase
where this would be a problem, but it just seems wrong.  Either we can in
the two spots replace return NULL_TREE; with { type = NULL_TREE; goto out; }
or we could perhaps abuse warning_sentinel or create a special class with
dtor to clean the flag up.

Let's use temp_override.


And lastly, I've fixed some formatting issues in the function while reading
it.

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

2020-03-14  Jakub Jelinek  <ja...@redhat.com>

        PR c++/90995
        * parser.c (cp_parser_enum_specifier): Make sure to restore
        parser->colon_corrects_to_scope_p in all cases.  If scoped
        enum or enum with underlying type is not followed by { or ;,
        call error_at rather than cp_parser_error.  Formatting fixes.

        * g++.dg/cpp0x/enum40.C: New test.

--- gcc/cp/parser.c.jj  2020-03-12 18:17:58.039230033 +0100
+++ gcc/cp/parser.c     2020-03-13 21:44:20.117085594 +0100
@@ -19043,24 +19043,24 @@ cp_parser_enum_specifier (cp_parser* par
push_deferring_access_checks (dk_no_check);
    nested_name_specifier
-      = cp_parser_nested_name_specifier_opt (parser,
-                                            /*typename_keyword_p=*/true,
-                                            /*check_dependency_p=*/false,
-                                            /*type_p=*/false,
-                                            /*is_declaration=*/false);
+    = cp_parser_nested_name_specifier_opt (parser,
+                                          /*typename_keyword_p=*/true,
+                                          /*check_dependency_p=*/false,
+                                          /*type_p=*/false,
+                                          /*is_declaration=*/false);
if (nested_name_specifier)
      {
        tree name;
identifier = cp_parser_identifier (parser);
-      name =  cp_parser_lookup_name (parser, identifier,
-                                    enum_type,
-                                    /*is_template=*/false,
-                                    /*is_namespace=*/false,
-                                    /*check_dependency=*/true,
-                                    /*ambiguous_decls=*/NULL,
-                                    input_location);
+      name = cp_parser_lookup_name (parser, identifier,
+                                   enum_type,
+                                   /*is_template=*/false,
+                                   /*is_namespace=*/false,
+                                   /*check_dependency=*/true,
+                                   /*ambiguous_decls=*/NULL,
+                                   input_location);
        if (name && name != error_mark_node)
        {
          type = TREE_TYPE (name);
@@ -19117,7 +19117,10 @@ cp_parser_enum_specifier (cp_parser* par
/* At this point this is surely not elaborated type specifier. */
        if (!cp_parser_parse_definitely (parser))
-       return NULL_TREE;
+       {
+         type = NULL_TREE;
+         goto out;
+       }
if (cxx_dialect < cxx11)
          maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS);
@@ -19151,17 +19154,23 @@ cp_parser_enum_specifier (cp_parser* par
        if ((scoped_enum_p || underlying_type)
          && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
        {
-         cp_parser_error (parser, "expected %<;%> or %<{%>");
          if (has_underlying_type)
            {
+             error_at (cp_lexer_peek_token (parser->lexer)->location,
+                       "expected %<;%> or %<{%>");
              type = NULL_TREE;
              goto out;
            }
+         else
+           cp_parser_error (parser, "expected %<;%> or %<{%>");
        }
      }
if (!has_underlying_type && !cp_parser_parse_definitely (parser))
-    return NULL_TREE;
+    {
+      type = NULL_TREE;
+      goto out;
+    }
if (nested_name_specifier)
      {
@@ -19172,9 +19181,7 @@ cp_parser_enum_specifier (cp_parser* par
          push_scope (nested_name_specifier);
        }
        else if (TREE_CODE (nested_name_specifier) == NAMESPACE_DECL)
-       {
-         push_nested_namespace (nested_name_specifier);
-       }
+       push_nested_namespace (nested_name_specifier);
      }
/* Issue an error message if type-definitions are forbidden here. */
--- gcc/testsuite/g++.dg/cpp0x/enum40.C.jj      2020-03-13 21:32:17.677763299 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/enum40.C 2020-03-13 21:31:51.283153431 +0100
@@ -0,0 +1,26 @@
+// PR c++/90995
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+  enum : int a alignas;                // { dg-error "expected" }
+}
+
+void
+bar ()
+{
+  enum : int a;                        // { dg-error "expected" }
+}
+
+void
+baz ()
+{
+  enum class a : int b alignas;        // { dg-error "expected" }
+}
+
+void
+qux ()
+{
+  enum class a : int b;                // { dg-error "expected" }
+}

        Jakub


Reply via email to