OSDN Git Service

(notice_cc_update): Set CC_FCOMI is this is a float compare.
[pf3gnuchains/gcc-fork.git] / gcc / c-common.c
index 492aa4b..d921661 100644 (file)
@@ -37,14 +37,19 @@ Boston, MA 02111-1307, USA.  */
 
 extern struct obstack permanent_obstack;
 
-enum attrs {A_PACKED, A_NOCOMMON, A_NORETURN, A_CONST, A_T_UNION,
+/* Nonzero means the expression being parsed will never be evaluated.
+   This is a count, since unevaluated expressions can nest.  */
+int skip_evaluation;
+
+enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
            A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
-           A_UNUSED, A_FORMAT, A_WEAK, A_ALIAS};
+           A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
 
 static void declare_hidden_char_array  PROTO((char *, char *));
 static void add_attribute              PROTO((enum attrs, char *,
                                               int, int, int));
 static void init_attributes            PROTO((void));
+static void record_international_format        PROTO((tree, tree, int));
 
 /* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__.  */
 
@@ -60,15 +65,12 @@ declare_function_name ()
     }
   else
     {
-      char *kind = "function";
-      if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE)
-       kind = "method";
       /* Allow functions to be nameless (such as artificial ones).  */
       if (DECL_NAME (current_function_decl))
         name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
       else
        name = "";
-      printable_name = (*decl_printable_name) (current_function_decl, &kind);
+      printable_name = (*decl_printable_name) (current_function_decl, 2);
     }
 
   declare_hidden_char_array ("__FUNCTION__", name);
@@ -263,6 +265,7 @@ init_attributes ()
 {
   add_attribute (A_PACKED, "packed", 0, 0, 0);
   add_attribute (A_NOCOMMON, "nocommon", 0, 0, 1);
+  add_attribute (A_COMMON, "common", 0, 0, 1);
   add_attribute (A_NORETURN, "noreturn", 0, 0, 1);
   add_attribute (A_NORETURN, "volatile", 0, 0, 1);
   add_attribute (A_UNUSED, "unused", 0, 0, 1);
@@ -274,6 +277,7 @@ init_attributes ()
   add_attribute (A_SECTION, "section", 1, 1, 1);
   add_attribute (A_ALIGNED, "aligned", 0, 1, 0);
   add_attribute (A_FORMAT, "format", 3, 3, 1);
+  add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1);
   add_attribute (A_WEAK, "weak", 0, 0, 1);
   add_attribute (A_ALIAS, "alias", 1, 1, 1);
 }
@@ -281,7 +285,7 @@ init_attributes ()
 /* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES
    and install them in NODE, which is either a DECL (including a TYPE_DECL)
    or a TYPE.  PREFIX_ATTRIBUTES can appear after the declaration specifiers
-   and declaration modifiers but before the declaration proper. */
+   and declaration modifiers but before the declaration proper.  */
 
 void
 decl_attributes (node, attributes, prefix_attributes)
@@ -321,6 +325,8 @@ decl_attributes (node, attributes, prefix_attributes)
          if (! valid_machine_attribute (name, args, decl, type))
            warning ("`%s' attribute directive ignored",
                     IDENTIFIER_POINTER (name));
+         else if (decl != 0)
+           type = TREE_TYPE (decl);
          continue;
        }
       else if (attrtab[i].decl_req && decl == 0)
@@ -358,6 +364,13 @@ decl_attributes (node, attributes, prefix_attributes)
            warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
          break;
 
+       case A_COMMON:
+         if (TREE_CODE (decl) == VAR_DECL)
+           DECL_COMMON (decl) = 1;
+         else
+           warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+         break;
+
        case A_NORETURN:
          if (TREE_CODE (decl) == FUNCTION_DECL)
            TREE_THIS_VOLATILE (decl) = 1;
@@ -396,7 +409,8 @@ decl_attributes (node, attributes, prefix_attributes)
          if (is_type
              && TREE_CODE (type) == UNION_TYPE
              && (decl == 0
-                 || TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type))))
+                 || (TYPE_FIELDS (type) != 0
+                     && TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type)))))
            TYPE_TRANSPARENT_UNION (type) = 1;
          else if (decl != 0 && TREE_CODE (decl) == PARM_DECL
                   && TREE_CODE (type) == UNION_TYPE
@@ -485,7 +499,8 @@ decl_attributes (node, attributes, prefix_attributes)
              && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
            {
              if (TREE_CODE (decl) == VAR_DECL 
-                 && current_function_decl != NULL_TREE)
+                 && current_function_decl != NULL_TREE
+                 && ! TREE_STATIC (decl))
                error_with_decl (decl,
                  "section attribute cannot be specified for local variables");
              /* The decl may have already been given a section attribute from
@@ -570,9 +585,15 @@ decl_attributes (node, attributes, prefix_attributes)
                         || !strcmp (IDENTIFIER_POINTER (format_type),
                                     "__scanf__")))
              is_scan = 1;
+           else if (TREE_CODE (format_type) == IDENTIFIER_NODE)
+             {
+               error ("`%s' is an unrecognized format function type",
+                      IDENTIFIER_POINTER (format_type));
+               continue;
+             }
            else
              {
-               error ("unrecognized format specifier for `%s'");
+               error ("unrecognized format specifier");
                continue;
              }
 
@@ -626,7 +647,7 @@ decl_attributes (node, attributes, prefix_attributes)
                if (first_arg_num != 0)
                  {
                    /* Verify that first_arg_num points to the last arg,
-                      the ... */
+                      the ...  */
                    while (argument)
                      arg_num++, argument = TREE_CHAIN (argument);
                  if (arg_num != first_arg_num)
@@ -643,13 +664,77 @@ decl_attributes (node, attributes, prefix_attributes)
            break;
          }
 
+       case A_FORMAT_ARG:
+         {
+           tree format_num_expr = TREE_VALUE (args);
+           int format_num, arg_num;
+           tree argument;
+       
+           if (TREE_CODE (decl) != FUNCTION_DECL)
+             {
+               error_with_decl (decl,
+                        "argument format specified for non-function `%s'");
+               continue;
+             }
+       
+           /* Strip any conversions from the first arg number and verify it
+              is a constant.  */
+           while (TREE_CODE (format_num_expr) == NOP_EXPR
+                  || TREE_CODE (format_num_expr) == CONVERT_EXPR
+                  || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
+             format_num_expr = TREE_OPERAND (format_num_expr, 0);
+
+           if (TREE_CODE (format_num_expr) != INTEGER_CST)
+             {
+               error ("format string has non-constant operand number");
+               continue;
+             }
+
+           format_num = TREE_INT_CST_LOW (format_num_expr);
+
+           /* If a parameter list is specified, verify that the format_num
+              argument is actually a string, in case the format attribute
+              is in error.  */
+           argument = TYPE_ARG_TYPES (type);
+           if (argument)
+             {
+               for (arg_num = 1; ; ++arg_num)
+                 {
+                   if (argument == 0 || arg_num == format_num)
+                     break;
+                   argument = TREE_CHAIN (argument);
+                 }
+               if (! argument
+                   || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
+                 || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
+                     != char_type_node))
+                 {
+                   error ("format string arg not a string type");
+                   continue;
+                 }
+             }
+
+           if (TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) != POINTER_TYPE
+               || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl))))
+                   != char_type_node))
+             {
+               error ("function does not return string type");
+               continue;
+             }
+
+           record_international_format (DECL_NAME (decl),
+                                        DECL_ASSEMBLER_NAME (decl),
+                                        format_num);
+           break;
+         }
+
        case A_WEAK:
          declare_weak (decl);
          break;
 
        case A_ALIAS:
          if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
-             || TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl))
+             || (TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl)))
            error_with_decl (decl,
                             "`%s' defined both normally and as an alias");
          else if (decl_function_context (decl) == 0)
@@ -668,6 +753,76 @@ decl_attributes (node, attributes, prefix_attributes)
        }
     }
 }
+
+/* Split SPECS_ATTRS, a list of declspecs and prefix attributes, into two
+   lists.  SPECS_ATTRS may also be just a typespec (eg: RECORD_TYPE).
+
+   The head of the declspec list is stored in DECLSPECS.
+   The head of the attribute list is stored in PREFIX_ATTRIBUTES.
+
+   Note that attributes in SPECS_ATTRS are stored in the TREE_PURPOSE of
+   the list elements.  We drop the containing TREE_LIST nodes and link the
+   resulting attributes together the way decl_attributes expects them.  */
+
+void
+split_specs_attrs (specs_attrs, declspecs, prefix_attributes)
+     tree specs_attrs;
+     tree *declspecs, *prefix_attributes;
+{
+  tree t, s, a, next, specs, attrs;
+
+  /* This can happen in c++ (eg: decl: typespec initdecls ';').  */
+  if (specs_attrs != NULL_TREE
+      && TREE_CODE (specs_attrs) != TREE_LIST)
+    {
+      *declspecs = specs_attrs;
+      *prefix_attributes = NULL_TREE;
+      return;
+    }
+
+  /* Remember to keep the lists in the same order, element-wise.  */
+
+  specs = s = NULL_TREE;
+  attrs = a = NULL_TREE;
+  for (t = specs_attrs; t; t = next)
+    {
+      next = TREE_CHAIN (t);
+      /* Declspecs have a non-NULL TREE_VALUE.  */
+      if (TREE_VALUE (t) != NULL_TREE)
+       {
+         if (specs == NULL_TREE)
+           specs = s = t;
+         else
+           {
+             TREE_CHAIN (s) = t;
+             s = t;
+           }
+       }
+      else
+       {
+         if (attrs == NULL_TREE)
+           attrs = a = TREE_PURPOSE (t);
+         else
+           {
+             TREE_CHAIN (a) = TREE_PURPOSE (t);
+             a = TREE_PURPOSE (t);
+           }
+         /* More attrs can be linked here, move A to the end.  */
+         while (TREE_CHAIN (a) != NULL_TREE)
+           a = TREE_CHAIN (a);
+       }
+    }
+
+  /* Terminate the lists.  */
+  if (s != NULL_TREE)
+    TREE_CHAIN (s) = NULL_TREE;
+  if (a != NULL_TREE)
+    TREE_CHAIN (a) = NULL_TREE;
+
+  /* All done.  */
+  *declspecs = specs;
+  *prefix_attributes = attrs;
+}
 \f
 /* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
    a parameter list.  */
@@ -739,7 +894,8 @@ static format_char_info scan_char_table[] = {
   { NULL }
 };
 
-typedef struct function_format_info {
+typedef struct function_format_info
+{
   struct function_format_info *next;  /* next structure on the list */
   tree name;                   /* identifier such as "printf" */
   tree assembler_name;         /* optional mangled identifier (for C++) */
@@ -750,14 +906,27 @@ typedef struct function_format_info {
 
 static function_format_info *function_format_list = NULL;
 
-static void check_format_info PROTO((function_format_info *, tree));
+typedef struct international_format_info
+{
+  struct international_format_info *next;  /* next structure on the list */
+  tree name;                   /* identifier such as "gettext" */
+  tree assembler_name;         /* optional mangled identifier (for C++) */
+  int format_num;              /* number of format argument */
+} international_format_info;
+
+static international_format_info *international_format_list = NULL;
+
+static void check_format_info          PROTO((function_format_info *, tree));
 
 /* Initialize the table of functions to perform format checking on.
    The ANSI functions are always checked (whether <stdio.h> is
    included or not), since it is common to call printf without
    including <stdio.h>.  There shouldn't be a problem with this,
    since ANSI reserves these function names whether you include the
-   header file or not.  In any case, the checking is harmless.  */
+   header file or not.  In any case, the checking is harmless. 
+
+   Also initialize the name of function that modify the format string for
+   internationalization purposes.  */
 
 void
 init_function_format_info ()
@@ -771,6 +940,10 @@ init_function_format_info ()
   record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
   record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
   record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
+
+  record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
+  record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
+  record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
 }
 
 /* Record information for argument format checking.  FUNCTION_IDENT is
@@ -779,7 +952,7 @@ init_function_format_info ()
    false indicates printf-style format checking.  FORMAT_NUM is the number
    of the argument which is the format control string (starting from 1).
    FIRST_ARG_NUM is the number of the first actual argument to check
-   against teh format string, or zero if no checking is not be done
+   against the format string, or zero if no checking is not be done
    (e.g. for varargs such as vfprintf).  */
 
 void
@@ -815,6 +988,43 @@ record_function_format (name, assembler_name, is_scan,
   info->first_arg_num = first_arg_num;
 }
 
+/* Record information for the names of function that modify the format
+   argument to format functions.  FUNCTION_IDENT is the identifier node for
+   the name of the function (its decl need not exist yet) and FORMAT_NUM is
+   the number of the argument which is the format control string (starting
+   from 1).  */
+
+static void
+record_international_format (name, assembler_name, format_num)
+      tree name;
+      tree assembler_name;
+      int format_num;
+{
+  international_format_info *info;
+
+  /* Re-use existing structure if it's there.  */
+
+  for (info = international_format_list; info; info = info->next)
+    {
+      if (info->name == name && info->assembler_name == assembler_name)
+       break;
+    }
+
+  if (! info)
+    {
+      info
+       = (international_format_info *)
+         xmalloc (sizeof (international_format_info));
+      info->next = international_format_list;
+      international_format_list = info;
+
+      info->name = name;
+      info->assembler_name = assembler_name;
+    }
+
+  info->format_num = format_num;
+}
+
 static char    tfaff[] = "too few arguments for format";
 \f
 /* Check the argument list of a call to printf, scanf, etc.
@@ -885,9 +1095,43 @@ check_format_info (info, params)
   params = TREE_CHAIN (params);
   if (format_tree == 0)
     return;
+
   /* We can only check the format if it's a string constant.  */
   while (TREE_CODE (format_tree) == NOP_EXPR)
     format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */
+
+  if (TREE_CODE (format_tree) == CALL_EXPR
+      && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR
+      && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0))
+         == FUNCTION_DECL))
+    {
+      tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0);
+
+      /* See if this is a call to a known internationalization function
+        that modifies the format arg.  */
+      international_format_info *info;
+
+      for (info = international_format_list; info; info = info->next)
+       if (info->assembler_name
+           ? (info->assembler_name == DECL_ASSEMBLER_NAME (function))
+           : (info->name == DECL_NAME (function)))
+         {
+           tree inner_args;
+           int i;
+
+           for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
+                inner_args != 0;
+                inner_args = TREE_CHAIN (inner_args), i++)
+             if (i == info->format_num)
+               {
+                 format_tree = TREE_VALUE (inner_args);
+
+                 while (TREE_CODE (format_tree) == NOP_EXPR)
+                   format_tree = TREE_OPERAND (format_tree, 0);
+               }
+         }
+    }
+
   if (integer_zerop (format_tree))
     {
       warning ("null format string");
@@ -1081,12 +1325,14 @@ check_format_info (info, params)
                }
            }
        }
-      if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'q' ||
-         *format_chars == 'L')
+      if (*format_chars == 'h' || *format_chars == 'l')
+       length_char = *format_chars++;
+      else if (*format_chars == 'q' || *format_chars == 'L')
        {
          length_char = *format_chars++;
          if (pedantic)
-           pedwarn ("ANSI C does not support the `q' length modifier");
+           pedwarn ("ANSI C does not support the `%c' length modifier",
+                    length_char);
        }
       else
        length_char = 0;
@@ -1192,7 +1438,7 @@ check_format_info (info, params)
              || format_char == 'x' || format_char == 'x'))
        {
          sprintf (message,
-                  "precision and `0' flag not both allowed with `%c' format",
+                  "`0' flag ignored with precision specifier and `%c' format",
                   format_char);
          warning (message);
        }
@@ -1357,7 +1603,8 @@ overflow_warning (value)
       && TREE_OVERFLOW (value))
     {
       TREE_OVERFLOW (value) = 0;
-      warning ("integer overflow in expression");
+      if (skip_evaluation == 0)
+       warning ("integer overflow in expression");
     }
   else if ((TREE_CODE (value) == REAL_CST
            || (TREE_CODE (value) == COMPLEX_CST
@@ -1365,7 +1612,8 @@ overflow_warning (value)
           && TREE_OVERFLOW (value))
     {
       TREE_OVERFLOW (value) = 0;
-      warning ("floating point overflow in expression");
+      if (skip_evaluation == 0)
+       warning ("floating point overflow in expression");
     }
 }
 
@@ -1381,6 +1629,7 @@ unsigned_conversion_warning (result, operand)
   if (TREE_CODE (operand) == INTEGER_CST
       && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
       && TREE_UNSIGNED (TREE_TYPE (result))
+      && skip_evaluation == 0
       && !int_fits_type_p (operand, TREE_TYPE (result)))
     {
       if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
@@ -1416,10 +1665,11 @@ convert_and_check (type, expr)
                && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))))
            /* If EXPR fits in the unsigned version of TYPE,
               don't warn unless pedantic.  */
-           if (pedantic
-               || TREE_UNSIGNED (type)
-               || ! int_fits_type_p (expr, unsigned_type (type)))
-             warning ("overflow in implicit constant conversion");
+           if ((pedantic
+                || TREE_UNSIGNED (type)
+                || ! int_fits_type_p (expr, unsigned_type (type)))
+               && skip_evaluation == 0)
+               warning ("overflow in implicit constant conversion");
        }
       else
        unsigned_conversion_warning (t, expr);
@@ -2070,6 +2320,12 @@ truthvalue_conversion (expr)
       return real_zerop (expr) ? boolean_false_node : boolean_true_node;
 
     case ADDR_EXPR:
+      /* If we are taking the address of a external decl, it might be zero
+        if it is weak, so we cannot optimize.  */
+      if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (expr, 0))) == 'd'
+         && DECL_EXTERNAL (TREE_OPERAND (expr, 0)))
+       break;
+
       if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
        return build (COMPOUND_EXPR, boolean_type_node,
                      TREE_OPERAND (expr, 0), boolean_true_node);
@@ -2112,7 +2368,7 @@ truthvalue_conversion (expr)
       if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
          || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
        break;
-      /* fall through... */
+      /* fall through...  */
     case NOP_EXPR:
       /* If this is widening the argument, we can ignore it.  */
       if (TYPE_PRECISION (TREE_TYPE (expr))
@@ -2126,7 +2382,7 @@ truthvalue_conversion (expr)
       if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
          && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE)
        break;
-      /* fall through... */
+      /* fall through...  */
     case BIT_XOR_EXPR:
       /* This and MINUS_EXPR can be changed into a comparison of the
         two objects.  */