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]

PATCH: __attribute__((nonnull))


Apparently, no-one wants to review that patch. Maybe with a more explicit
subject line, it will look nicer...
Anyway, after some fidgeting, I implemented the whole thing.

What this does:
* remove non-null pointer checking from format(printf), since there exist
some printf-like functions that accept NULL pointers (BSD4.4 warn, for 
instance).

* generalize the record_international/record_format framework so that any
number of attributes can be chained for a given function--might even be
useful per-se, as it is logically meaningful for a function to be
vprintf-like in its first arguments, and scanf-like in the remaining part...

* add a separate nonnull attribute, for functions that explicitly demand
non-null pointers.

* implement a shortcut, where attribute((nonnull)), without numeric
parameters, means that all pointer arguments for that function have the 
non-null property.

This patch just warns about explicit null constants.  I think that it might
be useful information for the compiler later on.
The extend.texi part only applies cleanly after my small documentation patch
(Subject: PATCH: reference attribute((format_arg)) properly.)

Wed Jan 12 02:08:26 CET 2000 Marc Espie	<espie@cvs.openbsd.org>
	* c-common.c (A_NONNULL):  New attribute.
	(init_attribute):  Record A_NONNULL.
	(record_function_format, record_international_format):  Remove.
	(insert_function_attribute):  New, replacement for record_xxx.
	(function_attribute_info, function_attributes_info):  New types.
	(new_function_format, new_international_format, new_nonnull_info):
	Corresponding constructors.
	(decl_attributes):  Handle A_NONNULL, modify A_FORMAT and
	A_FORMAT_ARG for new framework.
	(function_format_info, international_format_info):  Fit with
	function_attribute(s)_info.
	(function_attribute_list):  New variable, replaces...
	(function_format_list, international_format_list):  Remove.
	(add_function_format):  New, helper for init_format_info.
	(init_format_info):  Adjust format attributes.
	(check_function_format):  Handle A_FORMAT and A_NONNULL attributes.
	(find_function_attribute, reduce_pointer):  New.
	(check_nonnull_info):  New, check A_NONNULL.
	(check_format_info):  Use helper functions, remove non-null pointer
	checks.
	* extend.texi: Document nonnull.

*** c-common.c.orig	Tue Jan 11 18:17:48 2000
--- c-common.c	Wed Jan 12 02:07:19 2000
*************** enum attrs {A_PACKED, A_NOCOMMON, A_COMM
*** 142,148 ****
  	    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_MALLOC,
! 	    A_NO_LIMIT_STACK};
  
  enum format_type { printf_format_type, scanf_format_type,
  		   strftime_format_type };
--- 142,148 ----
  	    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_MALLOC,
! 	    A_NONNULL, A_NO_LIMIT_STACK};
  
  enum format_type { printf_format_type, scanf_format_type,
  		   strftime_format_type };
*************** static void declare_hidden_char_array	PR
*** 151,159 ****
  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));
  
--- 151,156 ----
*************** init_attributes ()
*** 479,484 ****
--- 476,482 ----
    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", 0, -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,
*** 506,511 ****
--- 504,531 ----
  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
*** 518,523 ****
--- 538,544 ----
    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
*** 551,556 ****
--- 572,578 ----
        tree args = TREE_VALUE (a);
        int i;
        enum attrs id;
+       int length;
  
        for (i = 0; i < attrtab_idx; i++)
  	if (attrtab[i].name == name)
*************** decl_attributes (node, attributes, prefi
*** 572,583 ****
  		   IDENTIFIER_POINTER (name));
  	  continue;
  	}
!       else if (list_length (args) < attrtab[i].min
! 	       || list_length (args) > attrtab[i].max)
! 	{
! 	  error ("wrong number of arguments specified for `%s' attribute",
! 		 IDENTIFIER_POINTER (name));
! 	  continue;
  	}
  
        id = attrtab[i].id;
--- 594,609 ----
  		   IDENTIFIER_POINTER (name));
  	  continue;
  	}
!       else
!         {
! 	  length = list_length (args);
!           if (length < attrtab[i].min 
! 	       || (attrtab[i].max != -1 && length > attrtab[i].max))
! 	    {
! 	      error ("wrong number of arguments specified for `%s' attribute",
! 		     IDENTIFIER_POINTER (name));
! 	      continue;
! 	    }
  	}
  
        id = attrtab[i].id;
*************** decl_attributes (node, attributes, prefi
*** 907,915 ****
  		  }
  	      }
  
! 	    record_function_format (DECL_NAME (decl),
! 				    DECL_ASSEMBLER_NAME (decl),
! 				    format_type, format_num, first_arg_num);
  	    break;
  	  }
  
--- 933,941 ----
  		  }
  	      }
  
! 	    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
*** 971,982 ****
  		continue;
  	      }
  
! 	    record_international_format (DECL_NAME (decl),
! 					 DECL_ASSEMBLER_NAME (decl),
! 					 format_num);
  	    break;
  	  }
  
  	case A_WEAK:
  	  declare_weak (decl);
  	  break;
--- 997,1091 ----
  		continue;
  	      }
  
! 	    list = insert_function_attribute (list, DECL_NAME (decl),
! 	      DECL_ASSEMBLER_NAME (decl),
! 	      new_international_format (format_num));
  	    break;
  	  }
  
+ 	case A_NONNULL:
+ 	  if (TREE_CODE (decl) != FUNCTION_DECL)
+ 	    {
+ 	      error_with_decl (decl,
+ 		       "nonnull attribute specified for non-function `%s'");
+ 	      continue;
+ 	    }
+       
+       	  /* No argument number: assume a full prototype, all pointers
+ 	     should be non null.  */
+ 	  if (!args)
+ 	    {
+ 	      tree argument;
+ 	      int arg_num;
+ 
+ 	      argument = TYPE_ARG_TYPES (type);
+ 	      if (!argument)
+ 	        {
+ 		  error_with_decl (decl, 
+ 		"nonnull attribute without arguments on a non-prototype `%s'");
+ 		  continue;
+ 		}
+ 	      for (arg_num = 1; ; ++arg_num)
+ 		{
+ 		  if (argument == 0)
+ 		    break;
+ 		  if (TREE_CODE (TREE_VALUE (argument)) == POINTER_TYPE)
+ 		    list = insert_function_attribute (list, 
+ 			    DECL_NAME (decl),
+ 			    DECL_ASSEMBLER_NAME (decl), 
+ 			    new_nonnull_info (arg_num));
+ 		  argument = TREE_CHAIN (argument);
+ 		}
+ 	      break;
+ 	    }
+ 
+ 	  for (; args; args = TREE_CHAIN (args))
+ 	    {
+ 	      tree argument;
+ 	      tree arg_num_expr = TREE_VALUE (args);
+ 	      int arg_num, ck_num;
+ 
+ 	    /* 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 ("nonnull argument 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 ("nonnull argument not a pointer type");
+ 		    continue;
+ 		  }
+ 	      }
+ 
+ 	    list = insert_function_attribute (list, DECL_NAME (decl),
+ 	      DECL_ASSEMBLER_NAME (decl), new_nonnull_info (arg_num));
+ 	    }
+ 	  break;
+ 
  	case A_WEAK:
  	  declare_weak (decl);
  	  break;
*************** static format_char_info time_char_table[
*** 1258,1286 ****
    { 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
--- 1367,1424 ----
    { NULL,		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
  };
  
+ 
+ static function_attributes_info *function_attributes_list = NULL;
+ 
+ /* Specialized flavors of function_attribute_info.  */
+ 
  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;
  
  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 non-null 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));
+ 
+ /* Helper function for setting up initial attribute for printf-like
+    functions, since the format argument is also non-null checked for
+    those.  */
+ 
+ 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));
+ }
  
  /* 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
*** 1295,1403 ****
  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
!    the identifier node for the name of the function to check (its decl
!    need not exist yet).
!    FORMAT_TYPE specifies the type of 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 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
     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 void
--- 1433,1568 ----
  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));
! }
! 
! /* Create information record for argument format checking.  FORMAT_TYPE 
!    specifies the type of 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 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);
  }
  
! /* Create information record for functions 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 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);
! }
  
! /* Create information record for functions with non-null parameters.
!    ARGUMENT_NUM is the number of the argument to check.  */
! 
! 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);
! }
  
! /* Record attribute information for the names of function.  Used as:
!  * newlist = insert_function_attribute (old, name, asm, new_xxx (...) );
!  * In reality, newlist == old if old != NULL, but clients don't need to
!  * depend on that.
!  */
! 
! 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;
  
! 	      /* Remove existing attributes.  Maybe we should have a flag
! 	       * to mark compiler built-in attributes ?  Or always warn if 
! 	       * we override existing attributes ?
! 	       */
! 	      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
*** 1418,1439 ****
       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.  */
--- 1583,1724 ----
       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;
  	}
      }
  }
  
+ /* Find a function attribute corresponding to given function names, and a
+    given attribute TYPE.  */
+  
+ 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;
+ }
+ 
+ /* Reduce an argument pointer to handle some non-trivial common cases.  */
+ 
+ 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);
+ 		}
+ 	  }
+     }
+ }
+ 
+ /* Check the argument list for non-null pointers.
+    INFO points to the nonnull_info structure.
+    PARAMS is the list of argument values.  */
+ 
+ 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);
+   if (arg == 0)
+     return;
+ 
+   arg = reduce_pointer (arg);
+ 
+   if (integer_zerop (arg))
+     {
+       warning ("null argument #%d", arg_num);
+       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)
*** 1474,1520 ****
    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)
      {
        /* The user may get multiple warnings if the supplied argument
--- 1759,1766 ----
    if (format_tree == 0)
      return;
  
!   format_tree = reduce_pointer (format_tree);
  
    if (TREE_CODE (format_tree) != ADDR_EXPR)
      {
        /* The user may get multiple warnings if the supplied argument
*** extend.texi.orig	Wed Jan 12 01:53:48 2000
--- extend.texi	Wed Jan 12 02:04:09 2000
*************** hack ((union foo) x);
*** 1315,1320 ****
--- 1315,1321 ----
  @cindex @code{const} applied to function
  @cindex functions with @code{printf}, @code{scanf} or @code{strftime} style arguments
  @cindex functions with a @code{gettext} style argument
+ @cindex functions with non-null pointer arguments
  @cindex functions that are passed arguments in registers on the 386
  @cindex functions that pop the argument stack on the 386
  @cindex functions that do not pop the argument stack on the 386
*************** carefully.
*** 1325,1335 ****
  
  The keyword @code{__attribute__} allows you to specify special
  attributes when making a declaration.  This keyword is followed by an
! attribute specification inside double parentheses.  Eleven attributes,
  @code{noreturn}, @code{const}, @code{format}, @code{format_arg},
  @code{no_instrument_function}, @code{section}, @code{constructor},
! @code{destructor}, @code{unused}, @code{weak} and @code{malloc} are
! currently defined for functions.  Other attributes, including
  @code{section} are supported for variables declarations (@pxref{Variable
  Attributes}) and for types (@pxref{Type Attributes}).
  
--- 1326,1337 ----
  
  The keyword @code{__attribute__} allows you to specify special
  attributes when making a declaration.  This keyword is followed by an
! attribute specification inside double parentheses.  Twelve attributes,
  @code{noreturn}, @code{const}, @code{format}, @code{format_arg},
  @code{no_instrument_function}, @code{section}, @code{constructor},
! @code{destructor}, @code{unused}, @code{weak}, @code{malloc} and
! @code{nonnull} are currently defined for functions.  
! Other attributes, including
  @code{section} are supported for variables declarations (@pxref{Variable
  Attributes}) and for types (@pxref{Type Attributes}).
  
*************** The @code{malloc} attribute is used to t
*** 1548,1553 ****
--- 1550,1580 ----
  may be treated as if it were the malloc function.  The compiler assumes
  that calls to malloc result in a pointers that cannot alias anything.
  This will often improve optimization.
+ 
+ @item nonnull (@var{arg-index,...})
+ @cindex @code{nonull} attribute
+ The @code{nonnull} attribute specifies that some function parameters should
+ be non null pointers.  For instance, the declaration:
+ 
+ @smallexample
+ extern void *
+ my_memcpy (void *dest, const void *src, size_t len) 
+         __attribute__ ((nonnull (1, 2)));
+ @end smallexample
+ 
+ @noindent
+ causes the compiler to check that, in calls to @code{my_memcpy}, 
+ arguments @var{dest} and @var{src} are non null.
+ 
+ Using @code{nonnull} without parameters is a shorthand that means that all
+ non pointer arguments should be non null, to be used with a full function
+ prototype only.  For instance, the example could be abbreviated to:
+ 
+ @smallexample
+ extern void *
+ my_memcpy (void *dest, const void *src, size_t len) 
+         __attribute__ ((nonnull));
+ @end smallexample
  
  @item alias ("target")
  @cindex @code{alias} attribute


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