(substitute_expr): Don't abort for RTL_EXPR and SAVE_EXPR; just do
[platform/upstream/gcc.git] / gcc / c-common.c
index 7e0691e..9a9c8ba 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines shared by all languages that are variants of C.
-   Copyright (C) 1992 Free Software Foundation, Inc.
+   Copyright (C) 1992, 1993 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -24,17 +24,18 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "flags.h"
 #include "obstack.h"
 #include <stdio.h>
+#include <ctype.h>
 
 extern struct obstack permanent_obstack;
 
+static void declare_hidden_char_array PROTO((char *, char *));
+
 /* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__.  */
 
 void
 declare_function_name ()
 {
-  tree decl, type, init;
   char *name, *printable_name;
-  int len;
 
   if (current_function_decl == NULL)
     {
@@ -54,42 +55,34 @@ declare_function_name ()
       printable_name = (*decl_printable_name) (current_function_decl, &kind);
     }
 
-  /* If the default size of char arrays isn't big enough for the name,
-     make a bigger one.  */
-  len = strlen (name) + 1;
-  type = char_array_type_node;
-  if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node)))
-      < len)
-    type = build_array_type (char_type_node,
-                            build_index_type (build_int_2 (len, 0)));
+  declare_hidden_char_array ("__FUNCTION__", name);
+  declare_hidden_char_array ("__PRETTY_FUNCTION__", printable_name);
+}
 
-  push_obstacks_nochange ();
-  decl = build_decl (VAR_DECL, get_identifier ("__FUNCTION__"), type);
-  TREE_STATIC (decl) = 1;
-  TREE_READONLY (decl) = 1;
-  DECL_SOURCE_LINE (decl) = 0;
-  DECL_IN_SYSTEM_HEADER (decl) = 1;
-  DECL_IGNORED_P (decl) = 1;
-  init = build_string (len, name);
-  TREE_TYPE (init) = type;
-  DECL_INITIAL (decl) = init;
-  finish_decl (pushdecl (decl), init, NULL_TREE);
+static void
+declare_hidden_char_array (name, value)
+     char *name, *value;
+{
+  tree decl, type, init;
+  int vlen;
 
-  len = strlen (printable_name) + 1;
+  /* If the default size of char arrays isn't big enough for the name,
+     make a bigger one.  */
+  vlen = strlen (value) + 1;
   type = char_array_type_node;
-  if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node)))
-      < len)
+  if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (type))) < vlen)
     type = build_array_type (char_type_node,
-                            build_index_type (build_int_2 (len, 0)));
+                            build_index_type (build_int_2 (vlen, 0)));
 
   push_obstacks_nochange ();
-  decl = build_decl (VAR_DECL, get_identifier ("__PRETTY_FUNCTION__"), type);
+  decl = build_decl (VAR_DECL, get_identifier (name), type);
   TREE_STATIC (decl) = 1;
   TREE_READONLY (decl) = 1;
+  TREE_ASM_WRITTEN (decl) = 1;
   DECL_SOURCE_LINE (decl) = 0;
   DECL_IN_SYSTEM_HEADER (decl) = 1;
   DECL_IGNORED_P (decl) = 1;
-  init = build_string (len, printable_name);
+  init = build_string (vlen, value);
   TREE_TYPE (init) = type;
   DECL_INITIAL (decl) = init;
   finish_decl (pushdecl (decl), init, NULL_TREE);
@@ -222,6 +215,17 @@ decl_attributes (decl, attributes)
        /* We can't set DECL_PACKED for a VAR_DECL, because the bit is
           used for DECL_REGISTER.  It wouldn't mean anything anyway.  */
       }
+    else if (TREE_VALUE (a) == get_identifier ("noreturn")
+            || TREE_VALUE (a) == get_identifier ("volatile"))
+      {
+       if (TREE_CODE (decl) == FUNCTION_DECL)
+         TREE_THIS_VOLATILE (decl) = 1;
+      }
+    else if (TREE_VALUE (a) == get_identifier ("const"))
+      {
+       if (TREE_CODE (decl) == FUNCTION_DECL)
+         TREE_READONLY (decl) = 1;
+      }
     else if (TREE_VALUE (a) != 0
             && TREE_CODE (TREE_VALUE (a)) == TREE_LIST
             && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("mode"))
@@ -253,8 +257,23 @@ decl_attributes (decl, attributes)
             && TREE_CODE (TREE_VALUE (a)) == TREE_LIST
             && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("aligned"))
       {
-       int align = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (a)))
-                   * BITS_PER_UNIT;
+       tree align_expr = TREE_VALUE (TREE_VALUE (a));
+       int align;
+
+       /* Strip any NOPs of any kind.  */
+       while (TREE_CODE (align_expr) == NOP_EXPR
+              || TREE_CODE (align_expr) == CONVERT_EXPR
+              || TREE_CODE (align_expr) == NON_LVALUE_EXPR)
+         align_expr = TREE_OPERAND (align_expr, 0);
+
+       if (TREE_CODE (align_expr) != INTEGER_CST)
+         {
+           error_with_decl (decl,
+                            "requested alignment of `%s' is not a constant");
+           continue;
+         }
+
+       align = TREE_INT_CST_LOW (align_expr) * BITS_PER_UNIT;
        
        if (exact_log2 (align) == -1)
          error_with_decl (decl,
@@ -272,8 +291,10 @@ decl_attributes (decl, attributes)
       {
         tree list = TREE_VALUE (TREE_VALUE (a));
         tree format_type = TREE_PURPOSE (list);
-       int format_num = TREE_INT_CST_LOW (TREE_PURPOSE (TREE_VALUE (list)));
-       int first_arg_num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
+       tree format_num_expr = TREE_PURPOSE (TREE_VALUE (list));
+       tree first_arg_num_expr = TREE_VALUE (TREE_VALUE (list));
+       int format_num;
+       int first_arg_num;
        int is_scan;
        tree argument;
        int arg_num;
@@ -282,7 +303,7 @@ decl_attributes (decl, attributes)
          {
            error_with_decl (decl,
                             "argument format specified for non-function `%s'");
-           return;
+           continue;
          }
        
        if (format_type == get_identifier ("printf"))
@@ -292,14 +313,36 @@ decl_attributes (decl, attributes)
        else
          {
            error_with_decl (decl, "unrecognized format specifier for `%s'");
-           return;
+           continue;
          }
        
+       /* Strip any conversions from the string index and first arg number
+          and verify they are constants.  */
+       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);
+
+       while (TREE_CODE (first_arg_num_expr) == NOP_EXPR
+              || TREE_CODE (first_arg_num_expr) == CONVERT_EXPR
+              || TREE_CODE (first_arg_num_expr) == NON_LVALUE_EXPR)
+         first_arg_num_expr = TREE_OPERAND (first_arg_num_expr, 0);
+
+       if (TREE_CODE (format_num_expr) != INTEGER_CST
+           || TREE_CODE (first_arg_num_expr) != INTEGER_CST)
+         {
+           error_with_decl (decl,
+                  "format string for `%s' has non-constant operand number");
+           continue;
+         }
+
+       format_num = TREE_INT_CST_LOW (format_num_expr);
+       first_arg_num = TREE_INT_CST_LOW (first_arg_num_expr);
        if (first_arg_num != 0 && first_arg_num <= format_num)
          {
            error_with_decl (decl,
-               "format string arg follows the args to be formatted, for `%s'");
-           return;
+             "format string arg follows the args to be formatted, for `%s'");
+           continue;
          }
 
        /* Verify that the format_num argument is actually a string, in case
@@ -318,7 +361,7 @@ decl_attributes (decl, attributes)
          {
            error_with_decl (decl,
                             "format string arg not a string type, for `%s'");
-           return;
+           continue;
          }
        if (first_arg_num != 0)
          {
@@ -329,15 +372,646 @@ decl_attributes (decl, attributes)
              {
                error_with_decl (decl,
                                 "args to be formatted is not ..., for `%s'");
-               return;
+               continue;
              }
          }
-       
-       record_format_info (DECL_NAME (decl), is_scan, format_num,
-                           first_arg_num);
+
+       record_function_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl),
+                               is_scan, format_num, first_arg_num);
       }
 }
 \f
+/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
+   a parameter list.  */
+
+#define T_I    &integer_type_node
+#define T_L    &long_integer_type_node
+#define T_S    &short_integer_type_node
+#define T_UI   &unsigned_type_node
+#define T_UL   &long_unsigned_type_node
+#define T_US   &short_unsigned_type_node
+#define T_F    &float_type_node
+#define T_D    &double_type_node
+#define T_LD   &long_double_type_node
+#define T_C    &char_type_node
+#define T_V    &void_type_node
+#define T_W    &wchar_type_node
+#define T_ST    &sizetype
+
+typedef struct {
+  char *format_chars;
+  int pointer_count;
+  /* Type of argument if no length modifier is used.  */
+  tree *nolen;
+  /* Type of argument if length modifier for shortening is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *hlen;
+  /* Type of argument if length modifier `l' is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *llen;
+  /* Type of argument if length modifier `L' is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *bigllen;
+  /* List of other modifier characters allowed with these options.  */
+  char *flag_chars;
+} format_char_info;
+
+static format_char_info print_char_table[] = {
+  { "di",      0,      T_I,    T_I,    T_L,    NULL,   "-wp0 +"        },
+  { "oxX",     0,      T_UI,   T_UI,   T_UL,   NULL,   "-wp0#"         },
+  { "u",       0,      T_UI,   T_UI,   T_UL,   NULL,   "-wp0"          },
+/* Two GNU extensions.  */
+  { "Z",       0,      T_ST,   NULL,   NULL,   NULL,   "-wp0"          },
+  { "m",       0,      T_UI,   T_UI,   T_UL,   NULL,   "-wp"           },
+  { "feEgG",   0,      T_D,    NULL,   NULL,   T_LD,   "-wp0 +#"       },
+  { "c",       0,      T_I,    NULL,   T_W,    NULL,   "-w"            },
+  { "C",       0,      T_W,    NULL,   NULL,   NULL,   "-w"            },
+  { "s",       1,      T_C,    NULL,   T_W,    NULL,   "-wp"           },
+  { "S",       1,      T_W,    NULL,   NULL,   NULL,   "-wp"           },
+  { "p",       1,      T_V,    NULL,   NULL,   NULL,   "-w"            },
+  { "n",       1,      T_I,    T_S,    T_L,    NULL,   ""              },
+  { NULL }
+};
+
+static format_char_info scan_char_table[] = {
+  { "di",      1,      T_I,    T_S,    T_L,    NULL,   "*"     },
+  { "ouxX",    1,      T_UI,   T_US,   T_UL,   NULL,   "*"     },      
+  { "efgEG",   1,      T_F,    NULL,   T_D,    T_LD,   "*"     },
+  { "sc",      1,      T_C,    NULL,   T_W,    NULL,   "*a"    },
+  { "[",       1,      T_C,    NULL,   NULL,   NULL,   "*a"    },
+  { "C",       1,      T_W,    NULL,   NULL,   NULL,   "*"     },
+  { "S",       1,      T_W,    NULL,   NULL,   NULL,   "*"     },
+  { "p",       2,      T_V,    NULL,   NULL,   NULL,   "*"     },
+  { "n",       1,      T_I,    T_S,    T_L,    NULL,   ""      },
+  { NULL }
+};
+
+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++) */
+  int is_scan;                 /* TRUE if *scanf */
+  int format_num;              /* number of format argument */
+  int first_arg_num;           /* number of first arg (zero for varargs) */
+} function_format_info;
+
+static function_format_info *function_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.  */
+
+void
+init_function_format_info ()
+{
+  record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2);
+  record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3);
+  record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3);
+  record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2);
+  record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3);
+  record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3);
+  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 information for argument format checking.  FUNCTION_IDENT is
+   the identifier node for the name of the function to check (its decl
+   need not exist yet).  IS_SCAN is true for scanf-type format checking;
+   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
+   (e.g. for varargs such as vfprintf).  */
+
+void
+record_function_format (name, assembler_name, is_scan,
+                       format_num, first_arg_num)
+      tree name;
+      tree assembler_name;
+      int is_scan;
+      int format_num;
+      int first_arg_num;
+{
+  function_format_info *info;
+
+  /* Re-use existing structure if it's there.  */
+
+  for (info = function_format_list; info; info = info->next)
+    {
+      if (info->name == name && info->assembler_name == assembler_name)
+       break;
+    }
+  if (! info)
+    {
+      info = (function_format_info *) xmalloc (sizeof (function_format_info));
+      info->next = function_format_list;
+      function_format_list = info;
+
+      info->name = name;
+      info->assembler_name = assembler_name;
+    }
+
+  info->is_scan = is_scan;
+  info->format_num = format_num;
+  info->first_arg_num = first_arg_num;
+}
+
+static char    tfaff[] = "too few arguments for format";
+\f
+/* Check the argument list of a call to printf, scanf, etc.
+   NAME is the function identifier.
+   ASSEMBLER_NAME is the function's assembler identifier.
+   (Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.)
+   PARAMS is the list of argument values.  */
+
+void
+check_function_format (name, assembler_name, params)
+     tree name;
+     tree assembler_name;
+     tree params;
+{
+  function_format_info *info;
+
+  /* See if this function is a format function.  */
+  for (info = function_format_list; info; info = info->next)
+    {
+      if (info->assembler_name
+         ? (info->assembler_name == assembler_name)
+         : (info->name == name))
+       {
+         /* Yup; check it.  */
+         check_format_info (info, params);
+         break;
+       }
+    }
+}
+
+/* Check the argument list of a call to printf, scanf, etc.
+   INFO points to the function_format_info structure.
+   PARAMS is the list of argument values.  */
+
+static void
+check_format_info (info, params)
+     function_format_info *info;
+     tree params;
+{
+  int i;
+  int arg_num;
+  int suppressed, wide, precise;
+  int length_char;
+  int format_char;
+  int format_length;
+  tree format_tree;
+  tree cur_param;
+  tree cur_type;
+  tree wanted_type;
+  tree first_fillin_param;
+  char *format_chars;
+  format_char_info *fci;
+  static char message[132];
+  char flag_chars[8];
+  int has_operand_number = 0;
+
+  /* Skip to format argument.  If the argument isn't available, there's
+     no work for us to do; prototype checking will catch the problem.  */
+  for (arg_num = 1; ; ++arg_num)
+    {
+      if (params == 0)
+       return;
+      if (arg_num == info->format_num)
+       break;
+      params = TREE_CHAIN (params);
+    }
+  format_tree = TREE_VALUE (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 (format_tree == null_pointer_node)
+    {
+      warning ("null format string");
+      return;
+    }
+  if (TREE_CODE (format_tree) != ADDR_EXPR)
+    return;
+  format_tree = TREE_OPERAND (format_tree, 0);
+  if (TREE_CODE (format_tree) != STRING_CST)
+    return;
+  format_chars = TREE_STRING_POINTER (format_tree);
+  format_length = TREE_STRING_LENGTH (format_tree);
+  if (format_length <= 1)
+    warning ("zero-length format string");
+  if (format_chars[--format_length] != 0)
+    {
+      warning ("unterminated format string");
+      return;
+    }
+  /* Skip to first argument to check.  */
+  while (arg_num + 1 < info->first_arg_num)
+    {
+      if (params == 0)
+       return;
+      params = TREE_CHAIN (params);
+      ++arg_num;
+    }
+
+  first_fillin_param = params;
+  while (1)
+    {
+      int aflag;
+      if (*format_chars == 0)
+       {
+         if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
+           warning ("embedded `\\0' in format");
+         if (info->first_arg_num != 0 && params != 0 && ! has_operand_number)
+           warning ("too many arguments for format");
+         return;
+       }
+      if (*format_chars++ != '%')
+       continue;
+      if (*format_chars == 0)
+       {
+         warning ("spurious trailing `%%' in format");
+         continue;
+       }
+      if (*format_chars == '%')
+       {
+         ++format_chars;
+         continue;
+       }
+      flag_chars[0] = 0;
+      suppressed = wide = precise = FALSE;
+      if (info->is_scan)
+       {
+         suppressed = *format_chars == '*';
+         if (suppressed)
+           ++format_chars;
+         while (isdigit (*format_chars))
+           ++format_chars;
+       }
+      else
+       {
+         /* See if we have a number followed by a dollar sign.  If we do,
+            it is an operand number, so set PARAMS to that operand.  */
+         if (*format_chars >= '0' && *format_chars <= '9')
+           {
+             char *p = format_chars;
+
+             while (*p >= '0' && *p++ <= '9')
+               ;
+
+             if (*p == '$')
+               {
+                 int opnum = atoi (format_chars);
+
+                 params = first_fillin_param;
+                 format_chars = p + 1;
+                 has_operand_number = 1;
+
+                 for (i = 1; i < opnum && params != 0; i++)
+                   params = TREE_CHAIN (params);
+
+                 if (opnum == 0 || params == 0)
+                   {
+                     warning ("operand number out of range in format");
+                     return;
+                   }
+               }
+           }
+
+         while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
+           {
+             if (index (flag_chars, *format_chars) != 0)
+               {
+                 sprintf (message, "repeated `%c' flag in format",
+                          *format_chars);
+                 warning (message);
+               }
+             i = strlen (flag_chars);
+             flag_chars[i++] = *format_chars++;
+             flag_chars[i] = 0;
+           }
+         /* "If the space and + flags both appear, 
+            the space flag will be ignored."  */
+         if (index (flag_chars, ' ') != 0
+             && index (flag_chars, '+') != 0)
+           warning ("use of both ` ' and `+' flags in format");
+         /* "If the 0 and - flags both appear,
+            the 0 flag will be ignored."  */
+         if (index (flag_chars, '0') != 0
+             && index (flag_chars, '-') != 0)
+           warning ("use of both `0' and `-' flags in format");
+         if (*format_chars == '*')
+           {
+             wide = TRUE;
+             /* "...a field width...may be indicated by an asterisk.
+                In this case, an int argument supplies the field width..."  */
+             ++format_chars;
+             if (params == 0)
+               {
+                 warning (tfaff);
+                 return;
+               }
+             if (info->first_arg_num != 0)
+               {
+                 cur_param = TREE_VALUE (params);
+                 params = TREE_CHAIN (params);
+                 ++arg_num;
+                 /* size_t is generally not valid here.
+                    It will work on most machines, because size_t and int
+                    have the same mode.  But might as well warn anyway,
+                    since it will fail on other machines.  */
+                 if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+                      != integer_type_node)
+                     &&
+                     (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+                      != unsigned_type_node))
+                   {
+                     sprintf (message,
+                              "field width is not type int (arg %d)",
+                              arg_num);
+                     warning (message);
+                   }
+               }
+           }
+         else
+           {
+             while (isdigit (*format_chars))
+               {
+                 wide = TRUE;
+                 ++format_chars;
+               }
+           }
+         if (*format_chars == '.')
+           {
+             precise = TRUE;
+             ++format_chars;
+             if (*format_chars != '*' && !isdigit (*format_chars))
+               warning ("`.' not followed by `*' or digit in format");
+             /* "...a...precision...may be indicated by an asterisk.
+                In this case, an int argument supplies the...precision."  */
+             if (*format_chars == '*')
+               {
+                 if (info->first_arg_num != 0)
+                   {
+                     ++format_chars;
+                     if (params == 0)
+                       {
+                         warning (tfaff);
+                         return;
+                       }
+                     cur_param = TREE_VALUE (params);
+                     params = TREE_CHAIN (params);
+                     ++arg_num;
+                     if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+                         != integer_type_node)
+                       {
+                         sprintf (message,
+                                  "field width is not type int (arg %d)",
+                                  arg_num);
+                         warning (message);
+                       }
+                   }
+               }
+             else
+               {
+                 while (isdigit (*format_chars))
+                   ++format_chars;
+               }
+           }
+       }
+      if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'L')
+       length_char = *format_chars++;
+      else
+       length_char = 0;
+      aflag = 0;
+      if (*format_chars == 'a')
+       {
+         aflag = 1;
+         format_chars++;
+       }
+      if (suppressed && length_char != 0)
+       {
+         sprintf (message,
+                  "use of `*' and `%c' together in format",
+                  length_char);
+         warning (message);
+       }
+      format_char = *format_chars;
+      if (format_char == 0)
+       {
+         warning ("conversion lacks type at end of format");
+         continue;
+       }
+      format_chars++;
+      fci = info->is_scan ? scan_char_table : print_char_table;
+      while (fci->format_chars != 0
+            && index (fci->format_chars, format_char) == 0)
+         ++fci;
+      if (fci->format_chars == 0)
+       {
+         if (format_char >= 040 && format_char < 0177)
+           sprintf (message,
+                    "unknown conversion type character `%c' in format",
+                    format_char);
+         else
+           sprintf (message,
+                    "unknown conversion type character 0x%x in format",
+                    format_char);
+         warning (message);
+         continue;
+       }
+      if (wide && index (fci->flag_chars, 'w') == 0)
+       {
+         sprintf (message, "width used with `%c' format",
+                  format_char);
+         warning (message);
+       }
+      if (precise && index (fci->flag_chars, 'p') == 0)
+       {
+         sprintf (message, "precision used with `%c' format",
+                  format_char);
+         warning (message);
+       }
+      if (aflag && index (fci->flag_chars, 'a') == 0)
+       {
+         sprintf (message, "`a' flag used with `%c' format",
+                  format_char);
+         warning (message);
+       }
+      if (info->is_scan && format_char == '[')
+       {
+         /* Skip over scan set, in case it happens to have '%' in it.  */
+         if (*format_chars == '^')
+           ++format_chars;
+         /* Find closing bracket; if one is hit immediately, then
+            it's part of the scan set rather than a terminator.  */
+         if (*format_chars == ']')
+           ++format_chars;
+         while (*format_chars && *format_chars != ']')
+           ++format_chars;
+         if (*format_chars != ']')
+             /* The end of the format string was reached.  */
+             warning ("no closing `]' for `%%[' format");
+       }
+      if (suppressed)
+       {
+         if (index (fci->flag_chars, '*') == 0)
+           {
+             sprintf (message,
+                      "suppression of `%c' conversion in format",
+                      format_char);
+             warning (message);
+           }
+         continue;
+       }
+      for (i = 0; flag_chars[i] != 0; ++i)
+       {
+         if (index (fci->flag_chars, flag_chars[i]) == 0)
+           {
+             sprintf (message, "flag `%c' used with type `%c'",
+                      flag_chars[i], format_char);
+             warning (message);
+           }
+       }
+      if (precise && index (flag_chars, '0') != 0
+         && (format_char == 'd' || format_char == 'i'
+             || format_char == 'o' || format_char == 'u'
+             || format_char == 'x' || format_char == 'x'))
+       {
+         sprintf (message,
+                  "precision and `0' flag not both allowed with `%c' format",
+                  format_char);
+         warning (message);
+       }
+      switch (length_char)
+       {
+       default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
+       case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
+       case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
+       case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
+       }
+      if (wanted_type == 0)
+       {
+         sprintf (message,
+                  "use of `%c' length character with `%c' type character",
+                  length_char, format_char);
+         warning (message);
+       }
+
+      /*
+       ** XXX -- should kvetch about stuff such as
+       **      {
+       **              const int       i;
+       **
+       **              scanf ("%d", &i);
+       **      }
+       */
+
+      /* Finally. . .check type of argument against desired type!  */
+      if (info->first_arg_num == 0)
+       continue;
+      if (params == 0)
+       {
+         warning (tfaff);
+         return;
+       }
+      cur_param = TREE_VALUE (params);
+      params = TREE_CHAIN (params);
+      ++arg_num;
+      cur_type = TREE_TYPE (cur_param);
+
+      /* Check the types of any additional pointer arguments
+        that precede the "real" argument.  */
+      for (i = 0; i < fci->pointer_count; ++i)
+       {
+         if (TREE_CODE (cur_type) == POINTER_TYPE)
+           {
+             cur_type = TREE_TYPE (cur_type);
+             continue;
+           }
+         sprintf (message,
+                  "format argument is not a %s (arg %d)",
+                  ((fci->pointer_count == 1) ? "pointer" : "pointer to a pointer"),
+                  arg_num);
+         warning (message);
+         break;
+       }
+
+      /* Check the type of the "real" argument, if there's a type we want.  */
+      if (i == fci->pointer_count && wanted_type != 0
+         && wanted_type != TYPE_MAIN_VARIANT (cur_type)
+         /* If we want `void *', allow any pointer type.
+            (Anything else would already have got a warning.)  */
+         && ! (wanted_type == void_type_node
+               && fci->pointer_count > 0)
+         /* Don't warn about differences merely in signedness.  */
+         && !(TREE_CODE (wanted_type) == INTEGER_TYPE
+              && TREE_CODE (TYPE_MAIN_VARIANT (cur_type)) == INTEGER_TYPE
+              && (TREE_UNSIGNED (wanted_type)
+                  ? wanted_type == (cur_type = unsigned_type (cur_type))
+                  : wanted_type == (cur_type = signed_type (cur_type))))
+         /* Likewise, "signed char", "unsigned char" and "char" are
+            equivalent but the above test won't consider them equivalent.  */
+         && ! (wanted_type == char_type_node
+               && (TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node
+                   || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node)))
+       {
+         register char *this;
+         register char *that;
+  
+         this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
+         that = 0;
+         if (TREE_CODE (cur_type) != ERROR_MARK
+             && TYPE_NAME (cur_type) != 0
+             && TREE_CODE (cur_type) != INTEGER_TYPE
+             && !(TREE_CODE (cur_type) == POINTER_TYPE
+                  && TREE_CODE (TREE_TYPE (cur_type)) == INTEGER_TYPE))
+           {
+             if (TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL
+                 && DECL_NAME (TYPE_NAME (cur_type)) != 0)
+               that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
+             else
+               that = IDENTIFIER_POINTER (TYPE_NAME (cur_type));
+           }
+
+         /* A nameless type can't possibly match what the format wants.
+            So there will be a warning for it.
+            Make up a string to describe vaguely what it is.  */
+         if (that == 0)
+           {
+             if (TREE_CODE (cur_type) == POINTER_TYPE)
+               that = "pointer";
+             else
+               that = "different type";
+           }
+
+         /* Make the warning better in case of mismatch of int vs long.  */
+         if (TREE_CODE (cur_type) == INTEGER_TYPE
+             && TREE_CODE (wanted_type) == INTEGER_TYPE
+             && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)
+             && TYPE_NAME (cur_type) != 0
+             && TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL)
+           that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
+
+         if (strcmp (this, that) != 0)
+           {
+             sprintf (message, "%s format, %s arg (arg %d)",
+                       this, that, arg_num);
+             warning (message);
+           }
+       }
+    }
+}
+\f
 /* Print a warning if a constant expression had overflow in folding.
    Invoke this function on every expression that the language
    requires to be a constant expression.
@@ -349,15 +1023,8 @@ constant_expression_warning (value)
      tree value;
 {
   if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value))
-    {
-      /* ??? This is a warning, not a pedwarn, in 2.4,
-        because it happens in contexts that are not
-        "constant expressions" in ANSI C.
-        Fix the problem differently in 2.5.  */
-      warning ("overflow in constant expression");
-      /* Suppress duplicate warnings.  */
-      TREE_CONSTANT_OVERFLOW (value) = 0;
-    }
+    if (pedantic)
+      pedwarn ("overflow in constant expression");
 }
 
 /* Print a warning if an expression had overflow in folding.
@@ -371,14 +1038,10 @@ void
 overflow_warning (value)
      tree value;
 {
-  if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value))
+  if (TREE_CODE (value) == INTEGER_CST && TREE_OVERFLOW (value))
     {
-      /* ??? This is a warning, not a pedwarn, in 2.4,
-        because it happens in contexts that are not
-        "constant expressions" in ANSI C.
-        Fix the problem differently in 2.5.  */
+      TREE_OVERFLOW (value) = 0;
       warning ("integer overflow in expression");
-      TREE_CONSTANT_OVERFLOW (value) = 0;
     }
 }
 
@@ -398,9 +1061,9 @@ unsigned_conversion_warning (result, operand)
     {
       if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
        /* This detects cases like converting -129 or 256 to unsigned char.  */
-       pedwarn ("large integer implicitly truncated to unsigned type");
+       warning ("large integer implicitly truncated to unsigned type");
       else if (warn_conversion)
-       pedwarn ("negative integer implicitly converted to unsigned type");
+       warning ("negative integer implicitly converted to unsigned type");
     }
 }
 
@@ -415,20 +1078,20 @@ convert_and_check (type, expr)
   tree t = convert (type, expr);
   if (TREE_CODE (t) == INTEGER_CST)
     {
-      if (TREE_UNSIGNED (TREE_TYPE (expr))
-         && !TREE_UNSIGNED (type)
-         && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
-         && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr)))
-       /* No warning for converting 0x80000000 to int.  */
-       TREE_CONSTANT_OVERFLOW (t) = 0;
-      else if (TREE_CONSTANT_OVERFLOW (t))
+      if (TREE_OVERFLOW (t))
        {
-         /* ??? This is a warning, not a pedwarn, in 2.4,
-            because it happens in contexts that are not
-            "constant expressions" in ANSI C.
-            Fix the problem differently in 2.5.  */
-         warning ("overflow in implicit constant conversion");
-         TREE_CONSTANT_OVERFLOW (t) = 0;
+         TREE_OVERFLOW (t) = 0;
+
+         /* No warning for converting 0x80000000 to int.  */
+         if (!(TREE_UNSIGNED (type) < TREE_UNSIGNED (TREE_TYPE (expr))
+               && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
+               && 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");
        }
       else
        unsigned_conversion_warning (t, expr);
@@ -640,6 +1303,8 @@ binary_op_error (code)
 /* Subroutine of build_binary_op, used for comparison operations.
    See if the operands have both been converted from subword integer types
    and, if so, perhaps change them both back to their original type.
+   This function is also responsible for converting the two operands
+   to the proper common type for comparison.
 
    The arguments of this function are all pointers to local variables
    of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1,
@@ -849,7 +1514,7 @@ shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
          type = unsigned_type (type);
        }
 
-      if (!max_gt && !unsignedp0)
+      if (!max_gt && !unsignedp0 && TREE_CODE (primop0) != INTEGER_CST)
        {
          /* This is the case of (char)x >?< 0x80, which people used to use
             expecting old C compilers to change the 0x80 into -0x80.  */
@@ -859,7 +1524,7 @@ shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
            warning ("comparison is always 1 due to limited range of data type");
        }
 
-      if (!min_lt && unsignedp0)
+      if (!min_lt && unsignedp0 && TREE_CODE (primop0) != INTEGER_CST)
        {
          /* This is the case of (unsigned char)x >?< -1 or < 0.  */
          if (val == integer_zero_node)
@@ -1039,7 +1704,7 @@ truthvalue_conversion (expr)
 
     case COMPLEX_EXPR:
       return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))
-                              ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR),
+                              ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
                              truthvalue_conversion (TREE_OPERAND (expr, 0)),
                              truthvalue_conversion (TREE_OPERAND (expr, 1)),
                              0);
@@ -1109,7 +1774,7 @@ truthvalue_conversion (expr)
   if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
     return (build_binary_op
            ((TREE_SIDE_EFFECTS (expr)
-             ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR),
+             ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
             truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)),
             truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)),
             0));
@@ -1207,16 +1872,13 @@ c_build_type_variant (type, constp, volatilep)
   if (TREE_CODE (type) == ARRAY_TYPE)
     {
       tree real_main_variant = TYPE_MAIN_VARIANT (type);
-      int permanent = TREE_PERMANENT (type);
 
-      if (permanent)
-       push_obstacks (&permanent_obstack, &permanent_obstack);
+      push_obstacks (TYPE_OBSTACK (type), TYPE_OBSTACK (type));
       type = build_array_type (c_build_type_variant (TREE_TYPE (type),
                                                     constp, volatilep),
                               TYPE_DOMAIN (type));
       TYPE_MAIN_VARIANT (type) = real_main_variant;
-      if (permanent)
-       pop_obstacks ();
+      pop_obstacks ();
     }
   return build_type_variant (type, constp, volatilep);
 }