]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/gengtype-parse.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / gengtype-parse.c
index 03ee7819b0f76dae66abf9250d05e7a5a006e67a..8ec83ce2dd85f2500ee0286f085c6588786cccf9 100644 (file)
@@ -1,5 +1,5 @@
 /* Process source files and output type information.
-   Copyright (C) 2006, 2007, 2010, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
-#ifdef GENERATOR_FILE
-#include "bconfig.h"
-#else
+#ifdef HOST_GENERATOR_FILE
 #include "config.h"
+#define GENERATOR_FILE 1
+#else
+#include "bconfig.h"
 #endif
 #include "system.h"
 #include "gengtype.h"
@@ -76,7 +77,6 @@ static const char *const token_names[] = {
   "union",
   "struct",
   "enum",
-  "VEC",
   "...",
   "ptr_alias",
   "nested_ptr",
@@ -87,6 +87,7 @@ static const char *const token_names[] = {
   "a string constant",
   "a character constant",
   "an array declarator",
+  "a C++ keyword to ignore"
 };
 
 /* This array is indexed by token code minus FIRST_TOKEN_WITH_VALUE.  */
@@ -98,6 +99,7 @@ static const char *const token_value_format[] = {
   "'\"%s\"'",
   "\"'%s'\"",
   "'[%s]'",
+  "'%s'",
 };
 
 /* Produce a printable representation for a token defined by CODE and
@@ -164,6 +166,21 @@ require (int t)
   return v;
 }
 
+/* As per require, but do not advance.  */
+static const char *
+require_without_advance (int t)
+{
+  int u = token ();
+  const char *v = T.value;
+  if (u != t)
+    {
+      parse_error ("expected %s, have %s",
+                  print_token (t, 0), print_token (u, v));
+      return 0;
+    }
+  return v;
+}
+
 /* If the next token does not have one of the codes T1 or T2, report a
    parse error; otherwise return the token's value.  */
 static const char *
@@ -181,6 +198,24 @@ require2 (int t1, int t2)
   return v;
 }
 
+/* If the next token does not have one of the codes T1, T2, T3 or T4, report a
+   parse error; otherwise return the token's value.  */
+static const char *
+require4 (int t1, int t2, int t3, int t4)
+{
+  int u = token ();
+  const char *v = advance ();
+  if (u != t1 && u != t2 && u != t3 && u != t4)
+    {
+      parse_error ("expected %s, %s, %s or %s, have %s",
+                  print_token (t1, 0), print_token (t2, 0),
+                  print_token (t3, 0), print_token (t4, 0),
+                  print_token (u, v));
+      return 0;
+    }
+  return v;
+}
+
 /* Near-terminals.  */
 
 /* C-style string constant concatenation: STRING+
@@ -212,7 +247,9 @@ string_seq (void)
 
 /* The caller has detected a template declaration that starts
    with TMPL_NAME.  Parse up to the closing '>'.  This recognizes
-   simple template declarations of the form ID<ID1,ID2,...,IDn>.
+   simple template declarations of the form ID<ID1,ID2,...,IDn>,
+   potentially with a single level of indirection e.g.
+     ID<ID1 *, ID2, ID3 *, ..., IDn>.
    It does not try to parse anything more sophisticated than that.
 
    Returns the template declaration string "ID<ID1,ID2,...,IDn>".  */
@@ -221,53 +258,70 @@ static const char *
 require_template_declaration (const char *tmpl_name)
 {
   char *str;
+  int num_indirections = 0;
 
   /* Recognize the opening '<'.  */
   require ('<');
   str = concat (tmpl_name, "<", (char *) 0);
 
   /* Read the comma-separated list of identifiers.  */
-  while (token () != '>')
+  int depth = 1;
+  while (depth > 0)
     {
-      const char *id = require2 (ID, ',');
+      if (token () == ENUM)
+       {
+         advance ();
+         str = concat (str, "enum ", (char *) 0);
+         continue;
+       }
+      if (token () == NUM
+         || token () == ':'
+         || token () == '+')
+       {
+         str = concat (str, advance (), (char *) 0);
+         continue;
+       }
+      if (token () == '<')
+       {
+         advance ();
+         str = concat (str, "<", (char *) 0);
+         depth += 1;
+         continue;
+       }
+      if (token () == '>')
+       {
+         advance ();
+         str = concat (str, ">", (char *) 0);
+         depth -= 1;
+         continue;
+       }
+      const char *id = require4 (SCALAR, ID, '*', ',');
       if (id == NULL)
-       id = ",";
+       {
+         if (T.code == '*')
+           {
+             id = "*";
+             if (num_indirections++)
+               parse_error ("only one level of indirection is supported"
+                            " in template arguments");
+           }
+         else
+           id = ",";
+       }
+      else
+       num_indirections = 0;
       str = concat (str, id, (char *) 0);
     }
-
-  /* Recognize the closing '>'.  */
-  require ('>');
-  str = concat (str, ">", (char *) 0);
-
   return str;
 }
 
 
-/* typedef_name: either an ID, or VEC(x,y), or a template type
-   specification of the form ID<t1,t2,...,tn>.
-
-   FIXME cxx-conversion.  VEC(x,y) is currently translated to the
-   template 'vec_t<x>'.  This is to support the transition to C++ and
-   avoid re-writing all the 'VEC(x,y)' declarations in the code.  This
-   needs to be fixed when the branch is merged into trunk.  */
+/* typedef_name: either an ID, or a template type
+   specification of the form ID<t1,t2,...,tn>.  */
 
 static const char *
 typedef_name (void)
 {
-  if (token () == VEC_TOKEN)
-    {
-      const char *c1, *r;
-      advance ();
-      require ('(');
-      c1 = require2 (ID, SCALAR);
-      require (',');
-      require (ID);
-      require (')');
-      r = concat ("vec_t<", c1, ">", (char *) 0);
-      free (CONST_CAST (char *, c1));
-      return r;
-    }
-
   const char *id = require (ID);
   if (token () == '<')
     return require_template_declaration (id);
@@ -313,78 +367,77 @@ consume_balanced (int opener, int closer)
 }
 
 /* Absorb a sequence of tokens, possibly including ()[]{}-delimited
-   expressions, until we encounter a semicolon outside any such
-   delimiters; absorb that too.  If IMMEDIATE is true, it is an error
-   if the semicolon is not the first token encountered.  */
+   expressions, until we encounter an end-of-statement marker (a ';' or
+   a '}') outside any such delimiters; absorb that too.  */
+
 static void
-consume_until_semi (bool immediate)
+consume_until_eos (void)
 {
-  if (immediate && token () != ';')
-    require (';');
   for (;;)
     switch (token ())
       {
       case ';':
        advance ();
        return;
-      default:
-       advance ();
-       break;
+
+      case '{':
+       consume_balanced ('{', '}');
+       return;
 
       case '(':
        consume_balanced ('(', ')');
        break;
+
       case '[':
        consume_balanced ('[', ']');
        break;
-      case '{':
-       consume_balanced ('{', '}');
-       break;
 
       case '}':
       case ']':
       case ')':
        parse_error ("unmatched '%c' while scanning for ';'", token ());
-      return;
+       return;
 
       case EOF_TOKEN:
        parse_error ("unexpected end of file while scanning for ';'");
        return;
+
+      default:
+       advance ();
+       break;
       }
 }
 
 /* Absorb a sequence of tokens, possibly including ()[]{}-delimited
    expressions, until we encounter a comma or semicolon outside any
-   such delimiters; absorb that too.  If IMMEDIATE is true, it is an
-   error if the comma or semicolon is not the first token encountered.
-   Returns true if the loop ended with a comma.  */
+   such delimiters; absorb that too.  Returns true if the loop ended
+   with a comma.  */
+
 static bool
-consume_until_comma_or_semi (bool immediate)
+consume_until_comma_or_eos ()
 {
-  if (immediate && token () != ',' && token () != ';')
-    require2 (',', ';');
   for (;;)
     switch (token ())
       {
       case ',':
        advance ();
        return true;
+
       case ';':
        advance ();
        return false;
-      default:
-       advance ();
-       break;
+
+      case '{':
+       consume_balanced ('{', '}');
+       return false;
 
       case '(':
        consume_balanced ('(', ')');
        break;
+
       case '[':
        consume_balanced ('[', ']');
        break;
-      case '{':
-       consume_balanced ('{', '}');
-       break;
 
       case '}':
       case ']':
@@ -396,6 +449,10 @@ consume_until_comma_or_semi (bool immediate)
       case EOF_TOKEN:
        parse_error ("unexpected end of file while scanning for ',' or ';'");
        return false;
+
+      default:
+       advance ();
+       break;
       }
 }
 \f
@@ -477,7 +534,6 @@ nestedptr_optvalue (options_p prev)
 /* One GTY(()) option:
    ID str_optvalue_opt
    | PTR_ALIAS type_optvalue
-   | PARAM_IS type_optvalue
    | NESTED_PTR nestedptr_optvalue
 */
 static options_p
@@ -492,9 +548,6 @@ option (options_p prev)
       advance ();
       return type_optvalue (prev, "ptr_alias");
 
-    case PARAM_IS:
-      return type_optvalue (prev, advance ());
-
     case NESTED_PTR:
       advance ();
       return nestedptr_optvalue (prev);
@@ -548,6 +601,8 @@ gtymarker_opt (void)
     return 0;
   return gtymarker ();
 }
+
+
 \f
 /* Declarators. The logic here is largely lifted from c-parser.c.
    Note that we do not have to process abstract declarators, which can
@@ -584,16 +639,21 @@ array_and_function_declarators_opt (type_p ty)
     return ty;
 }
 
-static type_p inner_declarator (type_p, const char **, options_p *);
+static type_p inner_declarator (type_p, const char **, options_p *, bool);
 
 /* direct_declarator:
    '(' inner_declarator ')'
+   '(' \epsilon ')'    <-- C++ ctors/dtors
    gtymarker_opt ID array_and_function_declarators_opt
 
    Subroutine of declarator, mutually recursive with inner_declarator;
-   do not use elsewhere.  */
+   do not use elsewhere.
+
+   IN_STRUCT is true if we are called while parsing structures or classes.  */
+
 static type_p
-direct_declarator (type_p ty, const char **namep, options_p *optsp)
+direct_declarator (type_p ty, const char **namep, options_p *optsp,
+                  bool in_struct)
 {
   /* The first token in a direct-declarator must be an ID, a
      GTY marker, or an open parenthesis.  */
@@ -602,18 +662,45 @@ direct_declarator (type_p ty, const char **namep, options_p *optsp)
     case GTY_TOKEN:
       *optsp = gtymarker ();
       /* fall through */
+
     case ID:
       *namep = require (ID);
+      /* If the next token is '(', we are parsing a function declaration.
+        Functions are ignored by gengtype, so we return NULL.  */
+      if (token () == '(')
+       return NULL;
       break;
 
     case '(':
+      /* If the declarator starts with a '(', we have three options.  We
+        are either parsing 'TYPE (*ID)' (i.e., a function pointer)
+        or 'TYPE(...)'.
+
+        The latter will be a constructor iff we are inside a
+        structure or class.  Otherwise, it could be a typedef, but
+        since we explicitly reject typedefs inside structures, we can
+        assume that we found a ctor and return NULL.  */
       advance ();
-      ty = inner_declarator (ty, namep, optsp);
+      if (in_struct && token () != '*')
+       {
+         /* Found a constructor.  Find and consume the closing ')'.  */
+         while (token () != ')')
+           advance ();
+         advance ();
+         /* Tell the caller to ignore this.  */
+         return NULL;
+       }
+      ty = inner_declarator (ty, namep, optsp, in_struct);
       require (')');
       break;
 
+    case IGNORABLE_CXX_KEYWORD:
+      /* Any C++ keyword like 'operator' means that we are not looking
+        at a regular data declarator.  */
+      return NULL;
+
     default:
-      parse_error ("expected '(', 'GTY', or an identifier, have %s",
+      parse_error ("expected '(', ')', 'GTY', or an identifier, have %s",
                   print_cur_token ());
       /* Do _not_ advance if what we have is a close squiggle brace, as
         we will get much better error recovery that way.  */
@@ -643,23 +730,26 @@ direct_declarator (type_p ty, const char **namep, options_p *optsp)
    direct_declarator
 
    Mutually recursive subroutine of direct_declarator; do not use
-   elsewhere.  */
+   elsewhere.
+
+   IN_STRUCT is true if we are called while parsing structures or classes.  */
 
 static type_p
-inner_declarator (type_p ty, const char **namep, options_p *optsp)
+inner_declarator (type_p ty, const char **namep, options_p *optsp,
+                 bool in_struct)
 {
   if (token () == '*')
     {
       type_p inner;
       advance ();
-      inner = inner_declarator (ty, namep, optsp);
+      inner = inner_declarator (ty, namep, optsp, in_struct);
       if (inner == 0)
        return 0;
       else
        return create_pointer (ty);
     }
   else
-    return direct_declarator (ty, namep, optsp);
+    return direct_declarator (ty, namep, optsp, in_struct);
 }
 
 /* declarator: '*'+ direct_declarator
@@ -667,10 +757,15 @@ inner_declarator (type_p ty, const char **namep, options_p *optsp)
    This is the sole public interface to this part of the grammar.
    Arguments are the type known so far, a pointer to where the name
    may be stored, and a pointer to where GTY options may be stored.
-   Returns the final type. */
+
+   IN_STRUCT is true when we are called to parse declarators inside
+   a structure or class.
+
+   Returns the final type.  */
 
 static type_p
-declarator (type_p ty, const char **namep, options_p *optsp)
+declarator (type_p ty, const char **namep, options_p *optsp,
+           bool in_struct = false)
 {
   *namep = 0;
   *optsp = 0;
@@ -679,7 +774,7 @@ declarator (type_p ty, const char **namep, options_p *optsp)
       advance ();
       ty = create_pointer (ty);
     }
-  return direct_declarator (ty, namep, optsp);
+  return direct_declarator (ty, namep, optsp, in_struct);
 }
 \f
 /* Types and declarations.  */
@@ -688,7 +783,7 @@ declarator (type_p ty, const char **namep, options_p *optsp)
    (
    type bitfield ';'
    | type declarator bitfield? ( ',' declarator bitfield? )+ ';'
-   )+
+   )*
 
    Knows that such declarations must end with a close brace (or,
    erroneously, at EOF).
@@ -702,24 +797,36 @@ struct_field_seq (void)
   const char *name;
   bool another;
 
-  do
+  while (token () != '}' && token () != EOF_TOKEN)
     {
       ty = type (&opts, true);
 
+      /* Ignore access-control keywords ("public:" etc).  */
+      while (!ty && token () == IGNORABLE_CXX_KEYWORD)
+       {
+         const char *keyword = advance ();
+         if (strcmp (keyword, "public:") != 0
+             && strcmp (keyword, "private:") != 0
+             && strcmp (keyword, "protected:") != 0)
+           break;
+         ty = type (&opts, true);
+       }
+
       if (!ty || token () == ':')
        {
-         consume_until_semi (false);
+         consume_until_eos ();
          continue;
        }
 
       do
        {
-         dty = declarator (ty, &name, &dopts);
+         dty = declarator (ty, &name, &dopts, true);
+
          /* There could be any number of weird things after the declarator,
             notably bitfield declarations and __attribute__s.  If this
             function returns true, the last thing was a comma, so we have
             more than one declarator paired with the current type.  */
-         another = consume_until_comma_or_semi (false);
+         another = consume_until_comma_or_eos ();
 
          if (!dty)
            continue;
@@ -733,13 +840,12 @@ struct_field_seq (void)
        }
       while (another);
     }
-  while (token () != '}' && token () != EOF_TOKEN);
   return nreverse_pairs (f);
 }
 
 /* Return true if OPTS contain the option named STR.  */
 
-static bool
+bool
 opts_have (options_p opts, const char *str)
 {
   for (options_p opt = opts; opt; opt = opt->next)
@@ -760,7 +866,12 @@ opts_have (options_p opts, const char *str)
    Returns a partial type; under some conditions (notably
    "struct foo GTY((...)) thing;") it may write an options
    structure to *OPTSP.
-*/
+
+   NESTED is true when parsing a declaration already known to have a
+   GTY marker. In these cases, typedef and enum declarations are not
+   allowed because gengtype only understands types at the global
+   scope.  */
+
 static type_p
 type (options_p *optsp, bool nested)
 {
@@ -773,13 +884,19 @@ type (options_p *optsp, bool nested)
       return create_scalar_type (s);
 
     case ID:
-    case VEC_TOKEN:
       s = typedef_name ();
       return resolve_typedef (s, &lexer_line);
 
+    case IGNORABLE_CXX_KEYWORD:
+      /* By returning NULL here, we indicate to the caller that they
+        should ignore everything following this keyword up to the
+        next ';' or '}'.  */
+      return NULL;
+
     case STRUCT:
     case UNION:
       {
+       type_p base_class = NULL;
        options_p opts = 0;
        /* GTY annotations follow attribute syntax
           GTY_BEFORE_ID is for union/struct declarations
@@ -796,8 +913,8 @@ type (options_p *optsp, bool nested)
        /* Top-level structures that are not explicitly tagged GTY(())
           are treated as mere forward declarations.  This is because
           there are a lot of structures that we don't need to know
-          about, and some of those have weird macro stuff in them
-          that we can't handle.  */
+          about, and some of those have C++ and macro constructs that
+          we cannot handle.  */
        if (nested || token () == GTY_TOKEN)
          {
            is_gty = GTY_BEFORE_ID;
@@ -819,9 +936,39 @@ type (options_p *optsp, bool nested)
            opts = gtymarker_opt ();
          }
 
+       bool is_user_gty = opts_have (opts, "user");
+
+       if (token () == ':')
+         {
+           if (is_gty && !is_user_gty)
+             {
+               /* For GTY-marked types that are not "user", parse some C++
+                  inheritance specifications.
+                  We require single-inheritance from a non-template type.  */
+               advance ();
+               const char *basename = require (ID);
+               /* This may be either an access specifier, or the base name.  */
+               if (strcmp (basename, "public") == 0
+                   || strcmp (basename, "protected") == 0
+                   || strcmp (basename, "private") == 0)
+                 basename = require (ID);
+               base_class = find_structure (basename, TYPE_STRUCT);
+               if (!base_class)
+                 parse_error ("unrecognized base class: %s", basename);
+               require_without_advance ('{');
+             }
+           else
+             {
+               /* For types lacking GTY-markings, skip over C++ inheritance
+                  specification (and thus avoid having to parse e.g. template
+                  types).  */
+               while (token () != '{')
+                 advance ();
+             }
+         }
+
        if (is_gty)
          {
-           bool is_user_gty = opts_have (opts, "user");
            if (token () == '{')
              {
                pair_p fields;
@@ -841,9 +988,11 @@ type (options_p *optsp, bool nested)
                    fields = NULL;
                    kind = TYPE_USER_STRUCT;
                    consume_balanced ('{', '}');
+                   return create_user_defined_type (s, &lexer_line);
                  }
 
-               return new_structure (s, kind, &lexer_line, fields, opts);
+               return new_structure (s, kind, &lexer_line, fields, opts,
+                                     base_class);
              }
          }
        else if (token () == '{')
@@ -853,6 +1002,21 @@ type (options_p *optsp, bool nested)
        return find_structure (s, kind);
       }
 
+    case TYPEDEF:
+      /* In C++, a typedef inside a struct/class/union defines a new
+        type for that inner scope.  We cannot support this in
+        gengtype because we have no concept of scoping.
+
+        We handle typedefs in the global scope separately (see
+        parse_file), so if we find a 'typedef', we must be inside
+        a struct.  */
+      gcc_assert (nested);
+      parse_error ("typedefs not supported in structures marked with "
+                  "automatic GTY markers.  Use GTY((user)) to mark "
+                  "this structure.");
+      advance ();
+      return NULL;
+
     case ENUM:
       advance ();
       if (token () == ID)
@@ -864,6 +1028,23 @@ type (options_p *optsp, bool nested)
 
       if (token () == '{')
        consume_balanced ('{', '}');
+
+      /* If after parsing the enum we are at the end of the statement,
+        and we are currently inside a structure, then this was an
+        enum declaration inside this scope.
+
+        We cannot support this for the same reason we cannot support
+        'typedef' inside structures (see the TYPEDEF handler above).
+        If this happens, emit an error and return NULL.  */
+      if (nested && token () == ';')
+       {
+         parse_error ("enum definitions not supported in structures marked "
+                      "with automatic GTY markers.  Use GTY((user)) to mark "
+                      "this structure.");
+         advance ();
+         return NULL;
+       }
+
       return create_scalar_type (s);
 
     default:
@@ -901,7 +1082,7 @@ typedef_decl (void)
 
       /* Yet another place where we could have junk (notably attributes)
         after the declarator.  */
-      another = consume_until_comma_or_semi (false);
+      another = consume_until_comma_or_eos ();
       if (dty)
        do_typedef (name, dty, &lexer_line);
     }