diff mbox

=?UTF-8?Q?Fix=20for=20PR=20objc/47832=20("[4.6=20Regression]=20ObjC=20er?= =?UTF-8?Q?rors=20on=20structures=20with=20flexible=20data=20members")?=

Message ID 1298316632.079627451@192.168.4.58
State New
Headers show

Commit Message

Nicola Pero Feb. 21, 2011, 7:30 p.m. UTC
GCC 4.6 tries to do a better job at catching invalid types used in instance
variables; in particular, it tries to catch structures with flexible data
members.  Ie,

struct A { int x; int[] y; }

@interface B
{
  struct A z;
}
@end

should be rejected.

Unfortunately, the check caused a regression (objc/47832) because of its
lack of sophistication.  The check worked by detecting when an array of
unknown size was being encoded as part of the generation of instance
variables.  That's all good, except that such encoding can happen even
if a pointer to such a structure is used as instance variable, as in --

struct A { int x; int[] y; }

@interface B
{
  struct A *z;
}
@end

which is perfectly valid code, but was rejected too (this is exactly
the regression described in PR objc/47832).

This patch fixes the problem by rearranging the check to happen earlier,
when the instance variable is added.  As an added bonus, we can print
the error on the correct line, and mention the invalid instance variable
too :-)

This patch was written for "stage 4", hence it tries to be as robust and
localized as possible.  I don't want to touch the C frontend or anything else.
I added ample comments to remind us that we can reorganize the code better
for 4.7.  I had ObjC++ skip the check altogether, so that there can't be
any regressions for ObjC++, but also, obviously, there is no check either.

Testcase included.

Ok to commit ?

Thanks

Comments

Mike Stump Feb. 22, 2011, 1:10 a.m. UTC | #1
On Feb 21, 2011, at 11:30 AM, Nicola Pero wrote:
> GCC 4.6 tries to do a better job at catching invalid types used in instance
> variables; in particular, it tries to catch structures with flexible data
> members.  Ie,

It doesn't really do what you claim you want to do:

> +      error ("instance variable %qs has unknown size", ivar_name);

You can to check:

	C_TYPE_VARIABLE_SIZE

if you want to see if the type in C has a variable size directly.

If you merely want to know if the size is known and constant:

	COMPLETE_TYPE_P (type) && TREE_CONSTANT (TYPE_SIZE (type)

will do that.  The difference between the two, if I understand it correctly, would be:

	const int i = 9;

	int a[i]

and

	int a[1<<63 * 1<<63]

Be sure to avoid C++, unless you know and understand templates...  The later will work in C++, if you ensure templates work as expected.  processing_template_decl can be used to figure out if templates are in play.
diff mbox

Patch

Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog      (revision 170370)
+++ objc/ChangeLog      (working copy)
@@ -1,10 +1,19 @@ 
 2011-02-20  Nicola Pero  <nicola.pero@meta-innovation.com>
 
-       * objc-gnu-runtime-abi-01.c (TARGET_64BIT): Removed.  Removed
-       usage of padding fields.  Do not include tm.h.
-       * objc-act.c (objc_write_global_declaration): Set input_location
-       to BUILTINS_LOCATION while generating runtime metadata.
+       PR objc/47832
+       * objc-act.c (flexible_array_type_p): New.
+       (add_instance_variable): Produce an error if an instance variable
+       is of flexible array type.
+       (encode_array): Do not emit an error if encoding a flexible array
+       type while generating instance variables.
 
+2011-02-20  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       * objc-gnu-runtime-abi-01.c (TARGET_64BIT): Removed.  Removed
+       usage of padding fields.  Do not include tm.h.
+       * objc-act.c (objc_write_global_declaration): Set input_location
+       to BUILTINS_LOCATION while generating runtime metadata.
+
 2011-01-20  Nicola Pero  <nicola.pero@meta-innovation.com>
 
        PR objc/47784
Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c     (revision 170370)
+++ objc/objc-act.c     (working copy)
@@ -5925,6 +5925,49 @@  add_category (tree klass, tree category)
     }
 }
 
+/* TODO: The check for flexible array members in instance variables is
+   implemented only for ObjC, and it is implemented by using this
+   flexible_array_type_p() function, which is basically lifted from
+   c-decl.c.  Is this the best way of doing the check ?  */
+
+/* flexible_array_type_p() seems to crash the compiler when used in
+   ObjC++, so we disable the check in ObjC++.  */
+#ifndef OBJCPLUS
+/* Determine whether TYPE is a structure with a flexible array member,
+   or a union containing such a structure (possibly recursively).  These
+   are invalid as instance variable.  */
+
+static bool
+flexible_array_type_p (tree type)
+{
+  tree x;
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      x = TYPE_FIELDS (type);
+      if (x == NULL_TREE)
+       return false;
+      while (DECL_CHAIN (x) != NULL_TREE)
+       x = DECL_CHAIN (x);
+      if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
+         && TYPE_SIZE (TREE_TYPE (x)) == NULL_TREE
+         && TYPE_DOMAIN (TREE_TYPE (x)) != NULL_TREE
+         && TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (x))) == NULL_TREE)
+       return true;
+      return false;
+    case UNION_TYPE:
+      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
+       {
+         if (flexible_array_type_p (TREE_TYPE (x)))
+           return true;
+       }
+      return false;
+    default:
+    return false;
+  }
+}
+#endif
+
 /* Called after parsing each instance variable declaration. Necessary to
    preserve typedefs and implement public/private...
 
@@ -5958,6 +6001,27 @@  add_instance_variable (tree klass, objc_ivar_visib
       return klass;
     }
 
+  /* Also, reject a struct with a flexible array member.  Ie,
+
+       struct A { int x; int[] y; };
+
+       @interface X
+       {
+         struct A instance_variable;
+       }
+       @end
+
+       is not valid as 'struct A' doesn't have a known size.  */
+#ifndef OBJCPLUS
+  /* TODO: Implemented this check for ObjC++ too.  */
+  if (flexible_array_type_p (field_type))
+    {
+      error ("instance variable %qs has unknown size", ivar_name);
+      /* Return class as is without adding this ivar.  */
+      return klass;      
+    }
+#endif
+
 #ifdef OBJCPLUS
   /* Check if the ivar being added has a non-POD C++ type.   If so, we will
      need to either (1) warn the user about it or (2) generate suitable
@@ -9926,27 +9990,23 @@  encode_array (tree type, int curtype, int format)
   if (an_int_cst == NULL)
     {
       /* We are trying to encode an incomplete array.  An incomplete
-        array is forbidden as part of an instance variable.  */
-      if (generating_instance_variables)
-       {
-         /* TODO: Detect this error earlier.  */
-         error ("instance variable has unknown size");
-         return;
-       }
+        array is forbidden as part of an instance variable; but it
+        may occur if the instance variable is a pointer to such an
+        array.  */
 
-      /* So the only case in which an incomplete array could occur is
-        if we are encoding the arguments or return value of a method.
-        In that case, an incomplete array argument or return value
-        (eg, -(void)display: (char[])string) is treated like a
-        pointer because that is how the compiler does the function
-        call.  A special, more complicated case, is when the
-        incomplete array is the last member of a struct (eg, if we
-        are encoding "struct { unsigned long int a;double b[];}"),
-        which is again part of a method argument/return value.  In
-        that case, we really need to communicate to the runtime that
-        there is an incomplete array (not a pointer!) there.  So, we
-        detect that special case and encode it as a zero-length
-        array.
+      /* So the only case in which an incomplete array could occur
+        (without being pointed to) is if we are encoding the
+        arguments or return value of a method.  In that case, an
+        incomplete array argument or return value (eg,
+        -(void)display: (char[])string) is treated like a pointer
+        because that is how the compiler does the function call.  A
+        special, more complicated case, is when the incomplete array
+        is the last member of a struct (eg, if we are encoding
+        "struct { unsigned long int a;double b[];}"), which is again
+        part of a method argument/return value.  In that case, we
+        really need to communicate to the runtime that there is an
+        incomplete array (not a pointer!) there.  So, we detect that
+        special case and encode it as a zero-length array.
 
         Try to detect that we are part of a struct.  We do this by
         searching for '=' in the type encoding for the current type.
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog (revision 170370)
+++ testsuite/ChangeLog (working copy)
@@ -1,3 +1,8 @@ 
+2011-02-21  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       PR objc/47832
+       * objc.dg/type-size-4.m: New test.
+
 2011-02-21  Jeff Law <law@redhat.com>
 
        PR rtl-optimization/46178
Index: testsuite/objc.dg/type-size-3.m
===================================================================
--- testsuite/objc.dg/type-size-3.m     (revision 170370)
+++ testsuite/objc.dg/type-size-3.m     (working copy)
@@ -10,11 +10,9 @@  typedef struct
 
 @interface Test
 {
-  test_type c;
+  test_type c; /* { dg-error "instance variable .c. has unknown size" } */
 }
 @end
 
 @implementation Test
 @end
-
-/* { dg-error "instance variable has unknown size" "" { target *-*-* } 0 } */
Index: testsuite/objc.dg/type-size-4.m
===================================================================
--- testsuite/objc.dg/type-size-4.m     (revision 0)
+++ testsuite/objc.dg/type-size-4.m     (revision 0)
@@ -0,0 +1,20 @@ 
+/* Allow ivars that are pointers to structs with an unknown size.  */
+/* Contributed by Nicola Pero  <nicola.pero@meta-innovation.com> */
+/* PR objc/47832 */
+/* { dg-do compile } */
+
+typedef struct
+{
+  unsigned long int a;
+  double b[];
+} test_type;
+
+@interface Test
+{
+  /* This is fine.  */
+  test_type *c;
+}
+@end
+
+@implementation Test
+@end