This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

gcc: Multiple checkers, to allow for warn() semantics


This is a fairly fun patch to gcc to improve function type recognition.
It all started then I noticed that gcc now warns for null pointers in 
printf-like functions, which is a bogus warning for BSD 4.4 warn(), 
of course.

I first started adding a warn_format_type, then I realized this was
probably not the right way to do it...

Specifically, checking for null pointers is a specific can of worms,
and so having 
__attribute__((nonull(arg_num))
sounded like a better way to check stuff.

Unfortunately, gcc's current code isn't really amenable to this kind of
check, as function attributes are specially encoded.

So the following patch makes it possible to chain attributes over a given
function prototype, and introduces a specific nonnull attribute to check
such parameters (specifically, a function prototype with attributes will
override the whole set of attributes defined previously).

This will slow gcc slightly, since both checks will now be independent,
so the parameter to check will be processed twice, but I believe there
are useful benefits.

Other functions such as strlen() could possibly benefit from that check,
though I didn't implement that.

This is not quite finished, obviously. I'd like some comments about the
idea's soundness, and various details I may have missed.

*** gcc/c-common.c.orig	Tue Jan  4 17:15:12 2000
--- gcc/c-common.c	Tue Jan  4 21:04:57 2000
*************** int skip_evaluation;
*** 54,60 ****
  enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
  	    A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION,
  	    A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
! 	    A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
  
  enum format_type { printf_format_type, scanf_format_type,
  		   strftime_format_type };
--- 54,60 ----
  enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
  	    A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION,
  	    A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
! 	    A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS, A_NONNULL};
  
  enum format_type { printf_format_type, scanf_format_type,
  		   strftime_format_type };
*************** static void declare_hidden_char_array	PR
*** 63,71 ****
  static void add_attribute		PROTO((enum attrs, const char *,
  					       int, int, int));
  static void init_attributes		PROTO((void));
- static void record_function_format	PROTO((tree, tree, enum format_type,
- 					       int, int));
- static void record_international_format	PROTO((tree, tree, int));
  static tree c_find_base_decl            PROTO((tree));
  static int default_valid_lang_attribute PROTO ((tree, tree, tree, tree));
  
--- 63,68 ----
*************** init_attributes ()
*** 391,396 ****
--- 388,394 ----
    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_NONNULL, "nonnull", 1, 1, 1);
    add_attribute (A_WEAK, "weak", 0, 0, 1);
    add_attribute (A_ALIAS, "alias", 1, 1, 1);
    add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1);
*************** default_valid_lang_attribute (attr_name,
*** 416,421 ****
--- 414,441 ----
  int (*valid_lang_attribute) PROTO ((tree, tree, tree, tree))
       = default_valid_lang_attribute;
  
+ typedef struct function_attribute_info
+ {
+   struct function_attribute_info *next;
+   enum attrs type;
+   /* data added there */
+ } function_attribute_info;
+ 
+ typedef struct function_attributes_info
+ {
+   struct function_attributes_info *next;  /* next structure on the list */
+   tree name;			/* identifier such as "printf" */
+   tree assembler_name;		/* optional mangled identifier (for C++) */
+   function_attribute_info *first;
+ } function_attributes_info;
+ 
+ static function_attribute_info *new_function_format 
+   PROTO((enum format_type, int, int));
+ static function_attribute_info *new_international_format PROTO((int));
+ static function_attribute_info *new_nonnull_info PROTO((int));
+ static function_attributes_info *insert_function_attribute
+   PROTO((function_attributes_info *, tree, tree, function_attribute_info *));
+ 
  /* 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
*************** decl_attributes (node, attributes, prefi
*** 428,433 ****
--- 448,454 ----
    tree decl = 0, type = 0;
    int is_type = 0;
    tree a;
+   function_attributes_info *list = NULL;
  
    if (attrtab_idx == 0)
      init_attributes ();
*************** decl_attributes (node, attributes, prefi
*** 810,818 ****
  		  }
  	      }
  
! 	    record_function_format (DECL_NAME (decl),
! 				    DECL_ASSEMBLER_NAME (decl),
! 				    format_type, format_num, first_arg_num);
  	    break;
  	  }
  
--- 831,840 ----
  		  }
  	      }
  
! 	    list = insert_function_attribute (list, 
! 	      DECL_NAME (decl),
! 	      DECL_ASSEMBLER_NAME (decl),
! 	      new_function_format (format_type, format_num, first_arg_num));
  	    break;
  	  }
  
*************** decl_attributes (node, attributes, prefi
*** 874,882 ****
  		continue;
  	      }
  
! 	    record_international_format (DECL_NAME (decl),
! 					 DECL_ASSEMBLER_NAME (decl),
! 					 format_num);
  	    break;
  	  }
  
--- 896,960 ----
  		continue;
  	      }
  
! 	    list = insert_function_attribute (list, DECL_NAME (decl),
! 	      DECL_ASSEMBLER_NAME (decl),
! 	      new_international_format (format_num));
! 	    break;
! 	  }
! 
! 	case A_NONNULL:
! 	  {
! 	    tree argument;
! 	    tree arg_num_expr = TREE_VALUE (args);
! 	    int arg_num, ck_num;
! 
! 	    if (TREE_CODE (decl) != FUNCTION_DECL)
! 	      {
! 		error_with_decl (decl,
! 			 "argument format specified for non-function `%s'");
! 		continue;
! 	      }
! 	
! 	    /* Strip any conversions from the arg number
! 	       and verify they are constants.  */
! 	    while (TREE_CODE (arg_num_expr) == NOP_EXPR
! 		   || TREE_CODE (arg_num_expr) == CONVERT_EXPR
! 		   || TREE_CODE (arg_num_expr) == NON_LVALUE_EXPR)
! 	      arg_num_expr = TREE_OPERAND (arg_num_expr, 0);
! 
! 
! 	    if (TREE_CODE (arg_num_expr) != INTEGER_CST)
! 	      {
! 		error ("format string has non-constant operand number");
! 		continue;
! 	      }
! 
! 	    arg_num = TREE_INT_CST_LOW (arg_num_expr);
! 
! 	    /* If a parameter list is specified, verify that the arg_num
! 	       argument is actually a pointer, in case the attribute
! 	       is in error.  */
! 	    argument = TYPE_ARG_TYPES (type);
! 	    if (argument)
! 	      {
! 		for (ck_num = 1; ; ++ck_num)
! 		  {
! 		    if (argument == 0 || ck_num == arg_num)
! 		      break;
! 		    argument = TREE_CHAIN (argument);
! 		  }
! 		if (! argument
! 		    || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE)
! 		  {
! 		    error ("non-null argument not a pointer type");
! 		    continue;
! 		  }
! 	      }
! 
! 	    list = insert_function_attribute (list, 
! 	      DECL_NAME (decl),
! 	      DECL_ASSEMBLER_NAME (decl),
! 	      new_nonnull_info (arg_num));
  	    break;
  	  }
  
*************** static format_char_info time_char_table[
*** 1144,1172 ****
    { NULL,		0, NULL, NULL, NULL, NULL, NULL, NULL, 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++) */
    enum format_type format_type;	/* type of format (printf, scanf, etc.) */
    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;
  
  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
--- 1222,1258 ----
    { NULL,		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
  };
  
+ 
  typedef struct function_format_info
  {
!   struct function_attribute_info *next;
!   enum attrs type;
    enum format_type format_type;	/* type of format (printf, scanf, etc.) */
    int format_num;		/* number of format argument */
    int first_arg_num;		/* number of first arg (zero for varargs) */
  } function_format_info;
  
! static function_attributes_info *function_attributes_list = NULL;
  
  typedef struct international_format_info
  {
!   struct function_attribute_info *next;
!   enum attrs type;
    int format_num;		/* number of format argument */
  } international_format_info;
  
! typedef struct nonnull_info
! {
!   struct function_attribute_info *next;
!   enum attrs type;
!   int argument_num;		/* number of format argument */
! } nonnull_info;
! 
! static function_attribute_info *find_function_attribute
!   PROTO((tree, tree, enum attrs));
  
  static void check_format_info		PROTO((function_format_info *, tree));
+ static void check_nonnull_info PROTO((nonnull_info *, tree));
  
  /* Initialize the table of functions to perform format checking on.
     The ANSI functions are always checked (whether <stdio.h> is
*************** static void check_format_info		PROTO((fu
*** 1178,1210 ****
     Also initialize the name of function that modify the format string for
     internationalization purposes.  */
  
  void
  init_function_format_info ()
  {
!   record_function_format (get_identifier ("printf"), NULL_TREE,
! 			  printf_format_type, 1, 2);
!   record_function_format (get_identifier ("fprintf"), NULL_TREE,
! 			  printf_format_type, 2, 3);
!   record_function_format (get_identifier ("sprintf"), NULL_TREE,
! 			  printf_format_type, 2, 3);
!   record_function_format (get_identifier ("scanf"), NULL_TREE,
! 			  scanf_format_type, 1, 2);
!   record_function_format (get_identifier ("fscanf"), NULL_TREE,
! 			  scanf_format_type, 2, 3);
!   record_function_format (get_identifier ("sscanf"), NULL_TREE,
! 			  scanf_format_type, 2, 3);
!   record_function_format (get_identifier ("vprintf"), NULL_TREE,
! 			  printf_format_type, 1, 0);
!   record_function_format (get_identifier ("vfprintf"), NULL_TREE,
! 			  printf_format_type, 2, 0);
!   record_function_format (get_identifier ("vsprintf"), NULL_TREE,
! 			  printf_format_type, 2, 0);
!   record_function_format (get_identifier ("strftime"), NULL_TREE,
! 			  strftime_format_type, 3, 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
--- 1264,1303 ----
     Also initialize the name of function that modify the format string for
     internationalization purposes.  */
  
+ static void
+ add_function_format (name, type, arg1, arg2)
+      const char *name;
+      enum format_type type;
+      int arg1;
+      int arg2;
+ {
+   tree t = get_identifier (name);
+   insert_function_attribute (insert_function_attribute (NULL, t, NULL_TREE,
+                                new_function_format(type, arg1, arg2)),
+ 			     t,
+ 			     NULL_TREE,
+ 			     new_nonnull_info (arg1));
+ }
+ 
  void
  init_function_format_info ()
  {
!   add_function_format ("printf", printf_format_type, 1, 2);
!   add_function_format ("fprintf", printf_format_type, 2, 3);
!   add_function_format ("sprintf", printf_format_type, 2, 3);
!   add_function_format ("scanf", scanf_format_type, 1, 2);
!   add_function_format ("fscanf", scanf_format_type, 2, 3);
!   add_function_format ("sscanf", scanf_format_type, 2, 3);
!   add_function_format ("vprintf", printf_format_type, 1, 0);
!   add_function_format ("vfprintf", printf_format_type, 2, 0);
!   add_function_format ("vsprintf", printf_format_type, 2, 0);
!   add_function_format ("strftime", strftime_format_type, 3, 0);
!   insert_function_attribute (NULL, get_identifier ("gettext"), NULL_TREE,
!     new_international_format (1));
!   insert_function_attribute (NULL, get_identifier ("dgettext"), NULL_TREE,
!     new_international_format (2));
!   insert_function_attribute (NULL, get_identifier ("dcgettext"), NULL_TREE, 
!     new_international_format (2));
  }
  
  /* Record information for argument format checking.  FUNCTION_IDENT is
*************** init_function_format_info ()
*** 1216,1252 ****
     against the format string, or zero if no checking is not be done
     (e.g. for varargs such as vfprintf).  */
  
! static void
! record_function_format (name, assembler_name, format_type,
  			format_num, first_arg_num)
-       tree name;
-       tree assembler_name;
        enum format_type format_type;
        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->format_type = format_type;
    info->format_num = format_num;
    info->first_arg_num = first_arg_num;
  }
  
  /* Record information for the names of function that modify the format
--- 1309,1330 ----
     against the format string, or zero if no checking is not be done
     (e.g. for varargs such as vfprintf).  */
  
! static function_attribute_info *
! new_function_format (format_type,
  			format_num, first_arg_num)
        enum format_type format_type;
        int format_num;
        int first_arg_num;
  {
    function_format_info *info;
  
!   info = (function_format_info *) xmalloc (sizeof (function_format_info));
!   info->next = NULL;
!   info->type = A_FORMAT;
    info->format_type = format_type;
    info->format_num = format_num;
    info->first_arg_num = first_arg_num;
+   return (function_attribute_info *) (info);
  }
  
  /* Record information for the names of function that modify the format
*************** record_function_format (name, assembler_
*** 1255,1289 ****
     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 void
--- 1333,1406 ----
     the number of the argument which is the format control string (starting
     from 1).  */
  
! static function_attribute_info *
! new_international_format (format_num)
        int format_num;
  {
    international_format_info *info;
  
!   info = (international_format_info *)
! 	  xmalloc (sizeof (international_format_info));
!   info->next = NULL;
!   info->type = A_FORMAT_ARG;
!   info->format_num = format_num;
!   return (function_attribute_info *) (info);
! }
  
! static function_attribute_info *
! new_nonnull_info (argument_num)
!       int argument_num;
! {
!   nonnull_info *info;
! 
!   info = (nonnull_info *)
! 	  xmalloc (sizeof (nonnull_info));
!   info->next = NULL;
!   info->type = A_NONNULL;
!   info->argument_num = argument_num;
!   return (function_attribute_info *) (info);
! }
  
! static function_attributes_info *
! insert_function_attribute (list, name, assembler_name, info)
! 	function_attributes_info *list;
! 	tree name;
! 	tree assembler_name;
! 	function_attribute_info *info;
! {
!   if (! list)
!   /* Re-use existing structure if it's there.  */
      {
!       for (list = function_attributes_list; list; list = list->next)
! 	{
! 	  if (list->name == name && list->assembler_name == assembler_name)
! 	    {
! 	      function_attribute_info *i, *j;
  
! 	      for (i = list->first; i; i = j)
! 	        {
! 		  j = i->next;
! 		  free (i);
! 		}
! 	      list->first = NULL;
! 	      break;
! 	    }
! 	}
      }
!   if (! list)
!     {
!       list = (function_attributes_info *) 
!              xmalloc (sizeof (function_format_info));
!       list->next = function_attributes_list;
!       function_attributes_list = list;
! 
!       list->name = name;
!       list->assembler_name = assembler_name;
!       list->first = NULL;
!     }
!   info->next = list->first;
!   list->first = info;
!   return list;
  }
  
  static void
*************** check_function_format (name, assembler_n
*** 1304,1325 ****
       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.  */
--- 1421,1554 ----
       tree assembler_name;
       tree params;
  {
!   function_attributes_info *info;
!   function_attribute_info *ck;
  
!   /* See if this function has attributes.  */
!   for (info = function_attributes_list; info; info = info->next)
      {
        if (info->assembler_name
  	  ? (info->assembler_name == assembler_name)
  	  : (info->name == name))
  	{
! 	  /* Yup; check those attributes.  */
! 	  for (ck = info->first; ck; ck = ck->next)
! 	    {
! 	      switch (ck->type)
! 	        {
! 		  case A_FORMAT:
! 		    check_format_info ((function_format_info *)ck, params);
! 		    break;
! 		  case A_NONNULL:
! 		    check_nonnull_info ((nonnull_info *)ck, params);
! 		    break;
! 		}
! 	    }
  	  break;
  	}
      }
  }
  
+ static function_attribute_info *
+ find_function_attribute(name, assembler_name, type)
+ 	tree name;
+ 	tree assembler_name;
+ 	enum attrs type;
+ {
+    function_attributes_info *info;
+    function_attribute_info *ck;
+ 
+    ck = NULL;
+ 
+    for (info = function_attributes_list; info; info = info->next)
+       if (info->assembler_name
+ 	  ? (info->assembler_name == assembler_name)
+ 	  : (info->name == name))
+         {
+ 	  for (ck = info->first; ck; ck = ck->next)
+ 	    {
+ 	      if (ck->type == type)
+ 		break;
+ 	    }
+ 	  break;
+ 	}
+     return ck;
+ }
+ 
+ static tree
+ reduce_pointer (arg)
+ 	tree arg;
+ {
+   /* We can only check the argument if it's a string constant.  */
+   while (TREE_CODE (arg) == NOP_EXPR)
+     arg = TREE_OPERAND (arg, 0); /* strip coercion */
+ 
+   if (TREE_CODE (arg) == CALL_EXPR
+       && TREE_CODE (TREE_OPERAND (arg, 0)) == ADDR_EXPR
+       && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (arg, 0), 0))
+ 	  == FUNCTION_DECL))
+     {
+       tree function = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ 
+       /* See if this is a call to a known internationalization function
+ 	 that modifies the format arg.  */
+       international_format_info *info;
+ 
+       info = (international_format_info *) 
+         find_function_attribute (DECL_NAME (function),
+ 	                         DECL_ASSEMBLER_NAME (function),
+ 				 A_FORMAT_ARG);
+       if (info)
+ 	  {
+ 	    tree inner_args;
+ 	    int i;
+ 
+ 	    for (inner_args = TREE_OPERAND (arg, 1), i = 1;
+ 		 inner_args != 0;
+ 		 inner_args = TREE_CHAIN (inner_args), i++)
+ 	      if (i == info->format_num)
+ 		{
+ 		  arg = TREE_VALUE (inner_args);
+ 
+ 		  while (TREE_CODE (arg) == NOP_EXPR)
+ 		    arg = TREE_OPERAND (arg, 0);
+ 		}
+ 	  }
+     }
+ }
+ 
+ static void
+ check_nonnull_info (info, params)
+      nonnull_info *info;
+      tree params;
+ {
+   tree arg;
+   int arg_num;
+ 
+   /* Skip to checked 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->argument_num)
+ 	break;
+       params = TREE_CHAIN (params);
+     }
+   arg = TREE_VALUE (params);
+   params = TREE_CHAIN (params);
+   if (arg == 0)
+     return;
+ 
+   arg = reduce_pointer (arg);
+ 
+   if (integer_zerop (arg))
+     {
+       warning ("null argument string");
+       return;
+     }
+ }
+ 
  /* 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.  */
*************** check_format_info (info, params)
*** 1360,1406 ****
    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");
-       return;
-     }
    if (TREE_CODE (format_tree) != ADDR_EXPR)
      return;
    format_tree = TREE_OPERAND (format_tree, 0);
--- 1589,1596 ----
    if (format_tree == 0)
      return;
  
!   format_tree = reduce_pointer (format_tree);
  
    if (TREE_CODE (format_tree) != ADDR_EXPR)
      return;
    format_tree = TREE_OPERAND (format_tree, 0);

-- 
	Marc Espie		
|anime, sf, juggling, unicycle, acrobatics, comics...
|AmigaOS, OpenBSD, C++, perl, Icon, PostScript...
| `real programmers don't die, they just get out of beta'

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]