diff mbox series

C-family: Only check the non-pointer data member

Message ID 20190113124838.29506-1-hjl.tools@gmail.com
State New
Headers show
Series C-family: Only check the non-pointer data member | expand

Commit Message

H.J. Lu Jan. 13, 2019, 12:48 p.m. UTC
When checking alignment of packed member, we should only check the
non-pointer data member.

gcc/c-family/

	PR c++/88664
	* c-family/c-warn.c (check_alignment_of_packed_member): Only
	check the non-pointer data member.

gcc/testsuite/

	PR c++/88664
	* gcc.dg/pr88664-1.c: New test.
	* g++.dg/pr88664-2.C: Likewise.
---
 gcc/c-family/c-warn.c            |  8 +++++---
 gcc/testsuite/g++.dg/pr88664-2.C | 21 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/pr88664-1.c | 19 +++++++++++++++++++
 3 files changed, 45 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/pr88664-2.C
 create mode 100644 gcc/testsuite/gcc.dg/pr88664-1.c

Comments

Jakub Jelinek Jan. 13, 2019, 1:20 p.m. UTC | #1
On Sun, Jan 13, 2019 at 04:48:38AM -0800, H.J. Lu wrote:
> When checking alignment of packed member, we should only check the
> non-pointer data member.
> 
> gcc/c-family/
> 
> 	PR c++/88664
> 	* c-family/c-warn.c (check_alignment_of_packed_member): Only
> 	check the non-pointer data member.

Doesn't that mean we'd stop warning about:
struct S { short s; void *p; } __attribute__ ((__packed__));

void **
foo (struct S *x)
{
  return static_cast <void **> (&x->p);
}
where we do want to warn?
What always matters is whether we take address of a packed structure
field/non-static data member or whether we just read that field.
The former should be warned about, the latter not.

	Jakub
H.J. Lu Jan. 13, 2019, 2:54 p.m. UTC | #2
On Sun, Jan 13, 2019 at 5:20 AM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Sun, Jan 13, 2019 at 04:48:38AM -0800, H.J. Lu wrote:
> > When checking alignment of packed member, we should only check the
> > non-pointer data member.
> >
> > gcc/c-family/
> >
> >       PR c++/88664
> >       * c-family/c-warn.c (check_alignment_of_packed_member): Only
> >       check the non-pointer data member.
>
> Doesn't that mean we'd stop warning about:
> struct S { short s; void *p; } __attribute__ ((__packed__));
>
> void **
> foo (struct S *x)
> {
>   return static_cast <void **> (&x->p);
> }
> where we do want to warn?
> What always matters is whether we take address of a packed structure
> field/non-static data member or whether we just read that field.
> The former should be warned about, the latter not.
>

How about this patch?  It checks if address is taken with NOP.
Jakub Jelinek Jan. 14, 2019, 2:22 p.m. UTC | #3
On Sun, Jan 13, 2019 at 06:54:05AM -0800, H.J. Lu wrote:
> > What always matters is whether we take address of a packed structure
> > field/non-static data member or whether we just read that field.
> > The former should be warned about, the latter not.
> >
> 
> How about this patch?  It checks if address is taken with NOP.

I'd like to first understand the convert_p argument to
warn_for_address_or_pointer_of_packed_member.

To me it seems you want to emit two different warnings, perhaps one
surpressed if the other one is emitted, but you actually from the start
decide which of the two you are going to check for.  That is just weird.

Consider -O2 -Waddress-of-packed-member -Wno-incompatible-pointer-types:

struct __attribute__((packed)) S { char p; int a, b, c; };

int *
foo (int x, struct S *p)
{
  return x ? &p->a : &p->b;
}

int *
bar (int x, struct S *p)
{
  return (int *) (x ? &p->a : &p->b);
}

short *
baz (int x, struct S *p)
{
  return x ? &p->a : &p->b;
}

short *
qux (int x, struct S *p)
{
  return (short *) (x ? &p->a : &p->b);
}

This warns in foo, bar and qux, but doesn't warn in baz, because we've
decided upfront that that case is convert_p = true.

I would have expected that the convert_p argument isn't passed at all,
the function always does the diagnostics about taking address that is
done with !convert_p right now, and either do the pointer -> pointer
conversion warning somewhere else (wherever we detect a pointer to pointer
conversion, even in the middle of expression?), or do it wherever you do
currently, but again always if the orig_rhs and type pointer types are
different.

	Jakub
H.J. Lu Jan. 14, 2019, 6 p.m. UTC | #4
On Mon, Jan 14, 2019 at 6:22 AM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Sun, Jan 13, 2019 at 06:54:05AM -0800, H.J. Lu wrote:
> > > What always matters is whether we take address of a packed structure
> > > field/non-static data member or whether we just read that field.
> > > The former should be warned about, the latter not.
> > >
> >
> > How about this patch?  It checks if address is taken with NOP.
>
> I'd like to first understand the convert_p argument to
> warn_for_address_or_pointer_of_packed_member.
>
> To me it seems you want to emit two different warnings, perhaps one
> surpressed if the other one is emitted, but you actually from the start
> decide which of the two you are going to check for.  That is just weird.

convert_p  is only for C.

> Consider -O2 -Waddress-of-packed-member -Wno-incompatible-pointer-types:
>
> struct __attribute__((packed)) S { char p; int a, b, c; };
>
> int *
> foo (int x, struct S *p)
> {
>   return x ? &p->a : &p->b;
> }
>
> int *
> bar (int x, struct S *p)
> {
>   return (int *) (x ? &p->a : &p->b);
> }
>
> short *
> baz (int x, struct S *p)
> {
>   return x ? &p->a : &p->b;
> }
>
> short *
> qux (int x, struct S *p)
> {
>   return (short *) (x ? &p->a : &p->b);
> }
>
> This warns in foo, bar and qux, but doesn't warn in baz, because we've
> decided upfront that that case is convert_p = true.
>
> I would have expected that the convert_p argument isn't passed at all,
> the function always does the diagnostics about taking address that is
> done with !convert_p right now, and either do the pointer -> pointer
> conversion warning somewhere else (wherever we detect a pointer to pointer
> conversion, even in the middle of expression?), or do it wherever you do
> currently, but again always if the orig_rhs and type pointer types are
> different.
>

When convert_p is true, we need to treat pointer conversion
as a special case.  I am testing this updated patch.
H.J. Lu Jan. 14, 2019, 11:23 p.m. UTC | #5
On Mon, Jan 14, 2019 at 10:00 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Mon, Jan 14, 2019 at 6:22 AM Jakub Jelinek <jakub@redhat.com> wrote:
> >
> > On Sun, Jan 13, 2019 at 06:54:05AM -0800, H.J. Lu wrote:
> > > > What always matters is whether we take address of a packed structure
> > > > field/non-static data member or whether we just read that field.
> > > > The former should be warned about, the latter not.
> > > >
> > >
> > > How about this patch?  It checks if address is taken with NOP.
> >
> > I'd like to first understand the convert_p argument to
> > warn_for_address_or_pointer_of_packed_member.
> >
> > To me it seems you want to emit two different warnings, perhaps one
> > surpressed if the other one is emitted, but you actually from the start
> > decide which of the two you are going to check for.  That is just weird.
>
> convert_p  is only for C.
>
> > Consider -O2 -Waddress-of-packed-member -Wno-incompatible-pointer-types:
> >
> > struct __attribute__((packed)) S { char p; int a, b, c; };
> >
> > int *
> > foo (int x, struct S *p)
> > {
> >   return x ? &p->a : &p->b;
> > }
> >
> > int *
> > bar (int x, struct S *p)
> > {
> >   return (int *) (x ? &p->a : &p->b);
> > }
> >
> > short *
> > baz (int x, struct S *p)
> > {
> >   return x ? &p->a : &p->b;
> > }
> >
> > short *
> > qux (int x, struct S *p)
> > {
> >   return (short *) (x ? &p->a : &p->b);
> > }
> >
> > This warns in foo, bar and qux, but doesn't warn in baz, because we've
> > decided upfront that that case is convert_p = true.
> >
> > I would have expected that the convert_p argument isn't passed at all,
> > the function always does the diagnostics about taking address that is
> > done with !convert_p right now, and either do the pointer -> pointer
> > conversion warning somewhere else (wherever we detect a pointer to pointer
> > conversion, even in the middle of expression?), or do it wherever you do
> > currently, but again always if the orig_rhs and type pointer types are
> > different.
> >
>
> When convert_p is true, we need to treat pointer conversion
> as a special case.  I am testing this updated patch.
>

There are no regressions with this patch:

https://gcc.gnu.org/ml/gcc-patches/2019-01/msg00792.html
Jakub Jelinek Jan. 16, 2019, 11:30 a.m. UTC | #6
On Mon, Jan 14, 2019 at 10:00:11AM -0800, H.J. Lu wrote:
> On Mon, Jan 14, 2019 at 6:22 AM Jakub Jelinek <jakub@redhat.com> wrote:
> >
> > On Sun, Jan 13, 2019 at 06:54:05AM -0800, H.J. Lu wrote:
> > > > What always matters is whether we take address of a packed structure
> > > > field/non-static data member or whether we just read that field.
> > > > The former should be warned about, the latter not.
> > > >
> > >
> > > How about this patch?  It checks if address is taken with NOP.
> >
> > I'd like to first understand the convert_p argument to
> > warn_for_address_or_pointer_of_packed_member.
> >
> > To me it seems you want to emit two different warnings, perhaps one
> > surpressed if the other one is emitted, but you actually from the start
> > decide which of the two you are going to check for.  That is just weird.
> 
> convert_p  is only for C.

Why?  What is so special about C and (implicit?) casts where the rhs isn't
ADDR_EXPR?  Aren't all casts (explicit or implicit) from one pointer type
to another pointer and satisfy the rules something that should be warned
about if the conditions are met?

	Jakub
H.J. Lu Jan. 16, 2019, 12:11 p.m. UTC | #7
On Wed, Jan 16, 2019 at 3:30 AM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Jan 14, 2019 at 10:00:11AM -0800, H.J. Lu wrote:
> > On Mon, Jan 14, 2019 at 6:22 AM Jakub Jelinek <jakub@redhat.com> wrote:
> > >
> > > On Sun, Jan 13, 2019 at 06:54:05AM -0800, H.J. Lu wrote:
> > > > > What always matters is whether we take address of a packed structure
> > > > > field/non-static data member or whether we just read that field.
> > > > > The former should be warned about, the latter not.
> > > > >
> > > >
> > > > How about this patch?  It checks if address is taken with NOP.
> > >
> > > I'd like to first understand the convert_p argument to
> > > warn_for_address_or_pointer_of_packed_member.
> > >
> > > To me it seems you want to emit two different warnings, perhaps one
> > > surpressed if the other one is emitted, but you actually from the start
> > > decide which of the two you are going to check for.  That is just weird.
> >
> > convert_p  is only for C.
>
> Why?  What is so special about C and (implicit?) casts where the rhs isn't
> ADDR_EXPR?  Aren't all casts (explicit or implicit) from one pointer type
> to another pointer and satisfy the rules something that should be warned

-Wincompatible-pointer-types is C only.   In C++,  incompatible pointer types
aren't allowed at all.

> about if the conditions are met?
>

convert_p is set to TRUE to handle this case for incompatible pointer types:

[hjl@gnu-cfl-1 pr51628-3]$ cat x.i
struct B { int i; };
struct C { struct B b; } __attribute__ ((packed));

extern struct C *p;

long* g8 (void) { return p; }
[hjl@gnu-cfl-1 pr51628-3]$ make x.s
/export/build/gnu/tools-build/gcc-debug/build-x86_64-linux/gcc/xgcc
-B/export/build/gnu/tools-build/gcc-debug/build-x86_64-linux/gcc/ -O2
-S x.i
x.i: In function \u2018g8\u2019:
x.i:6:26: warning: returning \u2018struct C *\u2019 from a function
with incompatible return type \u2018long int *\u2019
[-Wincompatible-pointer-types]
    6 | long* g8 (void) { return p; }
      |                          ^
x.i:6:1: warning: converting a packed \u2018struct C *\u2019 pointer
(alignment 1) to \u2018long int *\u2019 (alignment 8) may result in an
unaligned pointer value [-Waddress-of-packed-member]
    6 | long* g8 (void) { return p; }
      | ^~~~
x.i:2:8: note: defined here
    2 | struct C { struct B b; } __attribute__ ((packed));
      |        ^
[hjl@gnu-cfl-1 pr51628-3]$

I am using the same function to warn both addresses and pointers.  I need a way
to tell that we are converting pointers, not assigning address to pointer.  This
info is readily available in the front-end.
Jakub Jelinek Jan. 16, 2019, 12:28 p.m. UTC | #8
On Wed, Jan 16, 2019 at 04:11:44AM -0800, H.J. Lu wrote:
> > Why?  What is so special about C and (implicit?) casts where the rhs isn't
> > ADDR_EXPR?  Aren't all casts (explicit or implicit) from one pointer type
> > to another pointer and satisfy the rules something that should be warned
> 
> -Wincompatible-pointer-types is C only.   In C++,  incompatible pointer types
> aren't allowed at all.

How so?  You can certainly:
struct B { int i; };
struct C { struct B b; } __attribute__ ((packed));

extern struct C *p;
long* g8 (void) { return (long *)p; }

and similarly for C.  So, why is explicit cast something that shouldn't
be warned about in this case and implicit cast should get a warning,
especially when it already does get one (and one even enabled by default,
-Wincompatible-pointer-types)?
Either such casts are problematic and then it should treat explicit and
implicit casts the same, or they aren't, and then
-Wincompatible-pointer-types is all you need.

	Jakub
Jakub Jelinek Jan. 16, 2019, 11:09 p.m. UTC | #9
On Mon, Jan 14, 2019 at 03:23:07PM -0800, H.J. Lu wrote:
> There are no regressions with this patch:
> 
> https://gcc.gnu.org/ml/gcc-patches/2019-01/msg00792.html

As the patch seems to be a step forward and fixes an important regression,
the patch is ok for trunk, but I'd like to keep discussions on the convert_p
stuff afterwards (see other mail).

	Jakub
H.J. Lu Jan. 17, 2019, 2:45 p.m. UTC | #10
On Wed, Jan 16, 2019 at 3:09 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Jan 14, 2019 at 03:23:07PM -0800, H.J. Lu wrote:
> > There are no regressions with this patch:
> >
> > https://gcc.gnu.org/ml/gcc-patches/2019-01/msg00792.html
>
> As the patch seems to be a step forward and fixes an important regression,
> the patch is ok for trunk, but I'd like to keep discussions on the convert_p
> stuff afterwards (see other mail).
>

I posted an updated patch:

https://gcc.gnu.org/ml/gcc-patches/2019-01/msg00982.html
diff mbox series

Patch

diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
index 070934ab2b6..97b92d3493a 100644
--- a/gcc/c-family/c-warn.c
+++ b/gcc/c-family/c-warn.c
@@ -2687,14 +2687,16 @@  warn_for_multistatement_macros (location_t body_loc, location_t next_loc,
 	    "this %qs clause", guard_tinfo_to_string (keyword));
 }
 
-/* Return struct or union type if the alignment of data memeber, FIELD,
-   is less than the alignment of TYPE.  Otherwise, return NULL_TREE.  */
+/* Return struct or union type if the alignment of non-pointer data
+   memeber, FIELD, is less than the alignment of TYPE.  Otherwise,
+   return NULL_TREE.  */
 
 static tree
 check_alignment_of_packed_member (tree type, tree field)
 {
-  /* Check alignment of the data member.  */
+  /* Check alignment of the non-pointer data member.  */
   if (TREE_CODE (field) == FIELD_DECL
+      && !POINTER_TYPE_P (TREE_TYPE (field))
       && (DECL_PACKED (field)
 	  || TYPE_PACKED (TREE_TYPE (field))))
     {
diff --git a/gcc/testsuite/g++.dg/pr88664-2.C b/gcc/testsuite/g++.dg/pr88664-2.C
new file mode 100644
index 00000000000..712600cdeb7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr88664-2.C
@@ -0,0 +1,21 @@ 
+/* PR c++/88664.  */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+struct epoll_event
+{
+  short events;
+  void *ptr;
+} __attribute__ ((__packed__));
+
+int *
+foo1 (epoll_event *event)
+{
+  return static_cast <int *> (event->ptr);
+}
+
+int *
+foo2 (epoll_event *event)
+{
+  return event ? static_cast <int *> (event->ptr) : (int *) 0;
+}
diff --git a/gcc/testsuite/gcc.dg/pr88664-1.c b/gcc/testsuite/gcc.dg/pr88664-1.c
new file mode 100644
index 00000000000..410167b542c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr88664-1.c
@@ -0,0 +1,19 @@ 
+/* PR c++/88664.  */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+struct data {
+  void *ptr;
+} __attribute__((packed)) var;
+
+int *
+fun1 (void)
+{
+  return (int *)var.ptr;
+}
+
+int *
+fun2 (int i)
+{
+  return i ? (int *)var.ptr : (int *) 0;
+}