[PATCH] __attribute__((nonnull))

Jason R Thorpe thorpej@wasabisystems.com
Sun May 19 11:06:00 GMT 2002


On Sun, May 19, 2002 at 12:02:35PM +0100, Joseph S. Myers wrote:

 > You're reimplementing the recursion used for format functions, but without
 > the recursion into gettext/ngettext functions.  I think it would be
 > desirable for the general recursion used here to be shared.  (At least if
 > it isn't shared, the old comment in check_format_info_recurse about how it
 > might be needs to go.)

Hm.  Okay.  Should the nonnull code be moved into c-format.c, then?  Or
should c-format.c export the recursion mechanism?

I was hoping to keep the null check stuff completely separate from the
format check stuff -- it just seemed more clean to do so.  (I mean,
decoupling them is really the whole point...)

...anyway, in the mean time, I have updated the comment in c-format.c.

 > The documentation needs to make it clear: is this attribute just for
 > *warning*, or may the compiler assume that a nonconstant argument, where
 > there is a nonnull attribute, must not be null, and optimise on that
 > basis?

Ok, I have updated the documentation to say that the compiler issues
a warning when it encounters a null being passed in an argument marked
as non-null.

(I certainly think using this information for optimization purposes is
a fine thing to do, but I have not implemented that, obviously...)

 > The patch lacks testcases.

Test cases added.  Updated patch attached.

       * c-common.c (c_common_attribute_table): Add "nonnull" attribute.
       (handle_nonnull_attribute, check_function_nonnull, nonnull_check_p,
       check_function_nonnull_recurse, get_nonnull_operand): New functions.
       * c-common.h (check_function_arguments, check_function_nonnull): New
       prototypes.
       * c-format.c (check_format_info_recurse): Update comment.
       * c-typeck.c (build_function_call): Call check_function_arguments
       instead of check_function_format.
       (check_function_arguments): New function.
       * doc/extend.texi: Document "nonnull" attribute.
       * testsuite/gcc.dg/nonnull-1.c: New test.

-- 
        -- Jason R. Thorpe <thorpej@wasabisystems.com>
-------------- next part --------------
Index: c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.326
diff -u -r1.326 c-common.c
--- c-common.c	18 May 2002 19:02:01 -0000	1.326
+++ c-common.c	19 May 2002 17:28:33 -0000
@@ -344,8 +344,14 @@
 						 bool *));
 static tree handle_vector_size_attribute PARAMS ((tree *, tree, tree, int,
 						  bool *));
+static tree handle_nonnull_attribute	PARAMS ((tree *, tree, tree, int,
+						 bool *));
 static tree vector_size_helper PARAMS ((tree, tree));
 
+static bool get_nonnull_operand		PARAMS ((tree, int *));
+static bool nonnull_check_p		PARAMS ((tree, int));
+static void check_function_nonnull_recurse PARAMS ((tree, int));
+
 /* Table of machine-independent attributes common to all C-like languages.  */
 const struct attribute_spec c_common_attribute_table[] =
 {
@@ -406,6 +412,8 @@
 			      handle_vector_size_attribute },
   { "visibility",	      1, 1, true,  false, false,
 			      handle_visibility_attribute },
+  { "nonnull",                0, -1, false, true, true,
+			      handle_nonnull_attribute },
   { NULL,                     0, 0, false, false, false, NULL }
 };
 
@@ -5595,4 +5603,176 @@
   TREE_THIS_VOLATILE (outer) = TREE_THIS_VOLATILE (type);
 
   return outer;
+}
+
+/* Handle the "nonnull" attribute.  */
+static tree
+handle_nonnull_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name ATTRIBUTE_UNUSED;
+     tree args;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  tree type = *node;
+  int attr_arg_num;
+
+  /* If no arguments are specified, all pointer arguments should be
+     non-null.  Veryify a full prototype is given so that the arguments
+     will have the correct types when we actually check them later.  */
+  if (! args)
+    {
+      if (! TYPE_ARG_TYPES (type))
+	{
+	  error ("nonnull attribute without arguments on a non-prototype");
+          *no_add_attrs = true;
+	}
+      return NULL_TREE;
+    }
+
+  /* Argument list specified.  Verify that each argument number references
+     a pointer argument.  */
+  for (attr_arg_num = 1; args; args = TREE_CHAIN (args))
+    {
+      tree argument;
+      int arg_num, ck_num;
+
+      if (! get_nonnull_operand (TREE_VALUE (args), &arg_num))
+	{
+	  error ("nonnull argument has invalid operand number (arg %d)",
+		 attr_arg_num);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+
+      argument = TYPE_ARG_TYPES (type);
+      if (argument)
+	{
+	  for (ck_num = 1; ; ck_num++)
+	    {
+	      if (! argument || ck_num == arg_num)
+		break;
+	      argument = TREE_CHAIN (argument);
+	    }
+
+	  if (! argument)
+	    {
+	      error ("nonnull argument has out of range operand number (arg %d, operand %d)",
+		     attr_arg_num, arg_num);
+	      *no_add_attrs = true;
+	      return NULL_TREE;
+	    }
+
+          if (TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE)
+	    {
+	      error ("nonnull argument references non-pointer operand (arg %d, operand %d)",
+		   attr_arg_num, arg_num);
+	      *no_add_attrs = true;
+	      return NULL_TREE;
+	    }
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* Check the argument list of a function call for null in argument slots
+   that are marked as requiring a non-null pointer argument.  */
+
+void
+check_function_nonnull (attrs, params)
+     tree attrs;
+     tree params;
+{
+  tree a, args;
+  int param_num;
+
+  /* See if this function has a nonnull attribute.  */
+  for (a = attrs; a; a = TREE_CHAIN (a))
+    if (is_attribute_p ("nonnull", TREE_PURPOSE (a)))
+      break;
+
+  if (! a)
+    return;
+
+  args = TREE_VALUE (a);
+
+  /* Walk the argument list.  If we encounter an argument number we
+     should check for non-null, do it.  If the attribute has no args,
+     then every pointer argument is checked.  */
+  for (param_num = 1; ; param_num++, params = TREE_CHAIN (params))
+    {
+      if (! params)
+	break;
+      if (! args || nonnull_check_p (args, param_num))
+	check_function_nonnull_recurse (TREE_VALUE (params), param_num);
+    }
+}
+
+static bool
+nonnull_check_p (args, param_num)
+     tree args;
+     int param_num;
+{
+  int arg_num;
+
+  for (; args; args = TREE_CHAIN (args))
+    {
+      if (! get_nonnull_operand (TREE_VALUE (args), &arg_num))
+        abort ();
+
+      if (arg_num == param_num)
+	return true;
+    }
+  return false;
+}
+
+static void
+check_function_nonnull_recurse (param, param_num)
+     tree param;
+     int param_num;
+{
+
+  if (TREE_CODE (param) == NOP_EXPR)
+    {
+      /* Strip coercion.  */
+      check_function_nonnull_recurse (TREE_OPERAND (param, 0), param_num);
+      return;
+    }
+
+  if (TREE_CODE (param) == COND_EXPR)
+    {
+      /* Check both halves of the conditional expression.  */
+      check_function_nonnull_recurse (TREE_OPERAND (param, 1), param_num);
+      check_function_nonnull_recurse (TREE_OPERAND (param, 2), param_num);
+      return;
+    }
+
+  if (TREE_CODE (TREE_TYPE (param)) != POINTER_TYPE)
+    return;
+
+  if (integer_zerop (param))
+    warning ("null argument where non-null required (arg %d)", param_num);
+}
+
+/* Helper for nonnull attribute handling; fetch the operand number
+   from the attribute argument list.  */
+static bool
+get_nonnull_operand (arg_num_expr, valp)
+     tree arg_num_expr;
+     int *valp;
+{
+  /* 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
+      || TREE_INT_CST_HIGH (arg_num_expr) != 0)
+    return false;
+
+  *valp = TREE_INT_CST_LOW (arg_num_expr);
+  return true;
 }
Index: c-common.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.h,v
retrieving revision 1.136
diff -u -r1.136 c-common.h
--- c-common.h	18 May 2002 19:02:01 -0000	1.136
+++ c-common.h	19 May 2002 17:28:34 -0000
@@ -522,7 +522,9 @@
 extern tree fname_decl				PARAMS ((unsigned, tree));
 extern const char *fname_string			PARAMS ((unsigned));
 
+extern void check_function_arguments		PARAMS ((tree, tree));
 extern void check_function_format		PARAMS ((int *, tree, tree));
+extern void check_function_nonnull		PARAMS ((tree, tree));
 extern void set_Wformat				PARAMS ((int));
 extern tree handle_format_attribute		PARAMS ((tree *, tree, tree,
 							 int, bool *));
Index: c-format.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-format.c,v
retrieving revision 1.21
diff -u -r1.21 c-format.c
--- c-format.c	18 May 2002 19:02:02 -0000	1.21
+++ c-format.c	19 May 2002 17:28:36 -0000
@@ -1475,12 +1475,9 @@
 
   if (integer_zerop (format_tree))
     {
-      /* FIXME: this warning should go away once Marc Espie's
-	 __attribute__((nonnull)) patch is in.  Instead, checking for
-	 nonnull attributes should probably change this function to act
-	 specially if info == NULL and add a res->number_null entry for
-	 that case, or maybe add a function pointer to be called at
-	 the end instead of hardcoding check_format_info_main.  */
+      /* FIXME: instead of warning about a null format string here,
+	 functions for which we want to perform this check should be
+	 marked with the "nonnull" attribute on the appropriate arguments.  */
       status_warning (status, "null format string");
 
       /* Skip to first argument to check, so we can see if this format
Index: c-typeck.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-typeck.c,v
retrieving revision 1.194
diff -u -r1.194 c-typeck.c
--- c-typeck.c	26 Apr 2002 21:56:55 -0000	1.194
+++ c-typeck.c	19 May 2002 17:28:51 -0000
@@ -1576,10 +1576,9 @@
   coerced_params
     = convert_arguments (TYPE_ARG_TYPES (fntype), params, name, fundecl);
 
-  /* Check for errors in format strings.  */
+  /* Check that the arguments to the function are valid.  */
 
-  if (warn_format)
-    check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);
+  check_function_arguments (TYPE_ATTRIBUTES (fntype), coerced_params);
 
   /* Recognize certain built-in functions so we can make tree-codes
      other than CALL_EXPR.  We do this when it enables fold-const.c
@@ -1603,6 +1602,23 @@
   if (VOID_TYPE_P (TREE_TYPE (result)))
     return result;
   return require_complete_type (result);
+}
+
+/* Check for valid arguments being passed to a function.  */
+void
+check_function_arguments (attrs, params)
+     tree attrs;
+     tree params;
+{
+  /* Check for null being passed in a pointer argument that must be
+     non-null. */
+
+  check_function_nonnull (attrs, params);
+
+  /* Check for errors in format strings.  */
+
+  if (warn_format)
+    check_function_format (NULL, attrs, params);
 }
 
 /* Convert the argument expressions in the list VALUES
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.70
diff -u -r1.70 extend.texi
--- doc/extend.texi	11 May 2002 16:25:04 -0000	1.70
+++ doc/extend.texi	19 May 2002 17:28:58 -0000
@@ -1868,6 +1868,7 @@
 @cindex @code{volatile} applied to function
 @cindex @code{const} applied to function
 @cindex functions with @code{printf}, @code{scanf}, @code{strftime} or @code{strfmon} style arguments
+@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
@@ -1884,11 +1885,11 @@
 @code{pure}, @code{const},
 @code{format}, @code{format_arg}, @code{no_instrument_function},
 @code{section}, @code{constructor}, @code{destructor}, @code{used},
-@code{unused}, @code{deprecated}, @code{weak}, @code{malloc}, and
-@code{alias}.  Several other attributes are defined for functions on
-particular target systems.  Other attributes, including @code{section}
-are supported for variables declarations (@pxref{Variable Attributes})
-and for types (@pxref{Type Attributes}).
+@code{unused}, @code{deprecated}, @code{weak}, @code{malloc},
+@code{alias}, and @code{nonnull}.  Several other attributes are defined
+for functions on particular target systems.  Other attributes, including
+@code{section} are supported for variables declarations
+(@pxref{Variable Attributes}) and for types (@pxref{Type Attributes}).
 
 You may also specify attributes with @samp{__} preceding and following
 each keyword.  This allows you to use them in header files without
@@ -2099,6 +2100,33 @@
 requested by @option{-ansi} or an appropriate @option{-std} option, or
 @option{-ffreestanding} is used.  @xref{C Dialect Options,,Options
 Controlling C Dialect}.
+
+@item nonnull (@var{arg-index,...})
+@cindex @code{nonnull} function 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.  If the compiler
+determines that a null pointer is passed in an argument slot marked
+as non-null, a warning is issued.
+
+If no argument index list is given to the @code{nonnull} attribute,
+all pointer arguments are marked as non-null.  To illustrate, the
+following declaration is equivalent to the previous example:
+
+@smallexample
+extern void *
+my_memcpy (void *dest, const void *src, size_t len)
+	__attribute__((nonnull));
+@end smallexample
 
 @item no_instrument_function
 @cindex @code{no_instrument_function} function attribute
Index: testsuite/gcc.dg/nonnull-1.c
===================================================================
RCS file: testsuite/gcc.dg/nonnull-1.c
diff -N testsuite/gcc.dg/nonnull-1.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gcc.dg/nonnull-1.c	19 May 2002 17:29:07 -0000
@@ -0,0 +1,32 @@
+/* Test for the "nonnull" function attribute.  */
+/* Origin: Jason Thorpe <thorpej@wasabisystems.com> */
+/* { dg-do compile } */
+
+#include <stddef.h>
+
+extern void func1 (char *, char *, int) __attribute__((nonnull));
+
+extern void func2 (char *, char *) __attribute__((nonnull(1)));
+
+extern void func3 (char *, int, char *, int)
+  __attribute__((nonnull(1,3)));
+
+void
+foo (int i1, int i2, int i3, char *cp1, char *cp2, char *cp3)
+{
+  func1(cp1, cp2, i1);
+
+  func1(NULL, cp2, i1); /* { dg-warning "null" "null with argless nonnull 1" } */
+  func1(cp1, NULL, i1); /* { dg-warning "null" "null with argless nonnull 2" } */
+  func1(cp1, cp2, 0);
+
+  func2(cp1, NULL);
+  func2(NULL, cp1); /* { dg-warning "null" "null with single explicit nonnull" } */
+
+  func3(NULL, i2, cp3, i3); /* { dg-warning "null" "null with explicit nonnull 1" } */
+  func3(cp3, i2, NULL, i3); /* { dg-warning "null" "null with explicit nonnull 3" } */
+
+  func1(i1 ? cp1 : NULL, cp2, i3); /* { dg-warning "null" "null with cond expr rhs" } */
+  func1(i1 ? NULL : cp1, cp2, i3); /* { dg-warning "null" "null with cond expr lhs" } */
+  func1(i1 ? (i2 ? cp1 : NULL) : cp2, cp3, i3); /* { dg-warning "null" "null with nested cond expr" } */
+}


More information about the Gcc-patches mailing list