diff mbox

C PATCH for c/70297 (crash with duplicate typedefs and -g)

Message ID 20160323135200.GF3401@redhat.com
State New
Headers show

Commit Message

Marek Polacek March 23, 2016, 1:52 p.m. UTC
On Mon, Mar 21, 2016 at 09:57:54PM +0100, Richard Biener wrote:
> On March 21, 2016 6:55:28 PM GMT+01:00, Marek Polacek <polacek@redhat.com> wrote:
> >This PR points out to a GC problem: when we freed a duplicate typedef,
> >we were
> >leaving its type in the variants list, with its TYPE_NAME still
> >pointing to the
> >now-freed TYPE_DECL, leading to a crash.   I was lucky to discover that
> >the
> >exact same problem was fixed in November for the C++ FE, so I could
> >just follow
> >suit here.  And because that change didn't add a new testcase, I'm
> >putting the
> >new test into c-c++-common/.
> >
> >Bootstrapped/regtested on x86_64-linux, ok for trunk/5?
> 
> But IIRC this will drop the aligned attribute effect as that applied to the new type?

Yes.  So this metamorphosed into another issue: what to do with

typedef int T;
typedef int T __attribute__((aligned (16)));
typedef int T __attribute__((aligned (32)));
?  Either we can reject this, or do what clang (and other compilers) do, that
is pick the strictest alignment - 32 in this case.  The following patch does
that.
I've also checked what the C FE does for a few other attributes:
packed
visibility
 - ignored on TYPE_DECLs
used
unused
deprecated
scalar_storage_order
 - we seem to do the right thing
transparent_union
 - typedef int T;
   typedef int T __attribute__((transparent_union));
   gives an error
vector_size
mode
 - typedef int T;
   typedef int T __attribute__((mode (SI)));
   works while
   typedef int T;
   typedef int T __attribute__((mode (DI)));
   results in "conflicts" error
   similarly for vector_size

Well, here's the patch.  Thoughts?

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2016-03-23  Marek Polacek  <polacek@redhat.com>

	PR c/70297
	* c-decl.c (merge_decls): Also set TYPE_ALIGN and TYPE_USER_ALIGN.

	* decl.c (duplicate_decls): Also set TYPE_ALIGN and TYPE_USER_ALIGN.

	* c-c++-common/pr70297.c: New test.
	* g++.dg/cpp0x/typedef-redecl.C: New test.
	* gcc.dg/typedef-redecl2.c: New test.


	Marek

Comments

Jeff Law March 29, 2016, 10:23 p.m. UTC | #1
On 03/23/2016 07:52 AM, Marek Polacek wrote:
> On Mon, Mar 21, 2016 at 09:57:54PM +0100, Richard Biener wrote:
>> On March 21, 2016 6:55:28 PM GMT+01:00, Marek Polacek <polacek@redhat.com> wrote:
>>> This PR points out to a GC problem: when we freed a duplicate typedef,
>>> we were
>>> leaving its type in the variants list, with its TYPE_NAME still
>>> pointing to the
>>> now-freed TYPE_DECL, leading to a crash.   I was lucky to discover that
>>> the
>>> exact same problem was fixed in November for the C++ FE, so I could
>>> just follow
>>> suit here.  And because that change didn't add a new testcase, I'm
>>> putting the
>>> new test into c-c++-common/.
>>>
>>> Bootstrapped/regtested on x86_64-linux, ok for trunk/5?
>>
>> But IIRC this will drop the aligned attribute effect as that applied to the new type?
>
> Yes.  So this metamorphosed into another issue: what to do with
>
> typedef int T;
> typedef int T __attribute__((aligned (16)));
> typedef int T __attribute__((aligned (32)));
> ?  Either we can reject this, or do what clang (and other compilers) do, that
> is pick the strictest alignment - 32 in this case.  The following patch does
> that.
> I've also checked what the C FE does for a few other attributes:
> packed
> visibility
>   - ignored on TYPE_DECLs
> used
> unused
> deprecated
> scalar_storage_order
>   - we seem to do the right thing
> transparent_union
>   - typedef int T;
>     typedef int T __attribute__((transparent_union));
>     gives an error
> vector_size
> mode
>   - typedef int T;
>     typedef int T __attribute__((mode (SI)));
>     works while
>     typedef int T;
>     typedef int T __attribute__((mode (DI)));
>     results in "conflicts" error
>     similarly for vector_size
>
> Well, here's the patch.  Thoughts?
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2016-03-23  Marek Polacek  <polacek@redhat.com>
>
> 	PR c/70297
> 	* c-decl.c (merge_decls): Also set TYPE_ALIGN and TYPE_USER_ALIGN.
>
> 	* decl.c (duplicate_decls): Also set TYPE_ALIGN and TYPE_USER_ALIGN.
>
> 	* c-c++-common/pr70297.c: New test.
> 	* g++.dg/cpp0x/typedef-redecl.C: New test.
> 	* gcc.dg/typedef-redecl2.c: New test.
OK.
jeff
diff mbox

Patch

diff --git gcc/c/c-decl.c gcc/c/c-decl.c
index bab4715..0dd2121 100644
--- gcc/c/c-decl.c
+++ gcc/c/c-decl.c
@@ -2358,6 +2358,35 @@  merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
   DECL_ATTRIBUTES (newdecl)
     = targetm.merge_decl_attributes (olddecl, newdecl);
 
+  /* For typedefs use the old type, as the new type's DECL_NAME points
+     at newdecl, which will be ggc_freed.  */
+  if (TREE_CODE (newdecl) == TYPE_DECL)
+    {
+      /* But NEWTYPE might have an attribute, honor that.  */
+      tree tem = newtype;
+      newtype = oldtype;
+
+      if (TYPE_USER_ALIGN (tem))
+	{
+	  if (TYPE_ALIGN (tem) > TYPE_ALIGN (newtype))
+	    TYPE_ALIGN (newtype) = TYPE_ALIGN (tem);
+	  TYPE_USER_ALIGN (newtype) = true;
+	}
+
+      /* And remove the new type from the variants list.  */
+      if (TYPE_NAME (TREE_TYPE (newdecl)) == newdecl)
+	{
+	  tree remove = TREE_TYPE (newdecl);
+	  for (tree t = TYPE_MAIN_VARIANT (remove); ;
+	       t = TYPE_NEXT_VARIANT (t))
+	    if (TYPE_NEXT_VARIANT (t) == remove)
+	      {
+		TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (remove);
+		break;
+	      }
+	}
+    }
+
   /* Merge the data types specified in the two decls.  */
   TREE_TYPE (newdecl)
     = TREE_TYPE (olddecl)
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 47a53cb..c21d321 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -2033,8 +2033,17 @@  duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 	 at newdecl, which will be ggc_freed.  */
       if (TREE_CODE (newdecl) == TYPE_DECL)
 	{
+	  /* But NEWTYPE might have an attribute, honor that.  */
+	  tree tem = TREE_TYPE (newdecl);
 	  newtype = oldtype;
 
+	  if (TYPE_USER_ALIGN (tem))
+	    {
+	      if (TYPE_ALIGN (tem) > TYPE_ALIGN (newtype))
+		TYPE_ALIGN (newtype) = TYPE_ALIGN (tem);
+	      TYPE_USER_ALIGN (newtype) = true;
+	    }
+
 	  /* And remove the new type from the variants list.  */
 	  if (TYPE_NAME (TREE_TYPE (newdecl)) == newdecl)
 	    {
diff --git gcc/testsuite/c-c++-common/pr70297.c gcc/testsuite/c-c++-common/pr70297.c
index e69de29..70a4f15 100644
--- gcc/testsuite/c-c++-common/pr70297.c
+++ gcc/testsuite/c-c++-common/pr70297.c
@@ -0,0 +1,9 @@ 
+/* PR c/70297 */
+/* { dg-do compile } */
+/* { dg-options "-g" } */
+
+typedef int T;
+typedef int T __attribute__((aligned (4)));
+struct S {
+  T *t;
+};
diff --git gcc/testsuite/g++.dg/cpp0x/typedef-redecl.C gcc/testsuite/g++.dg/cpp0x/typedef-redecl.C
index e69de29..576c7ce 100644
--- gcc/testsuite/g++.dg/cpp0x/typedef-redecl.C
+++ gcc/testsuite/g++.dg/cpp0x/typedef-redecl.C
@@ -0,0 +1,12 @@ 
+// PR c/70297
+// { dg-do compile { target c++11 } }
+
+#define N 64
+
+typedef int T;
+typedef int T __attribute__((aligned (N)));
+typedef int T __attribute__((aligned (N * 2)));
+typedef int T __attribute__((aligned (N)));
+typedef int T;
+
+static_assert (alignof (T) == N * 2, "N * 2");
diff --git gcc/testsuite/gcc.dg/typedef-redecl2.c gcc/testsuite/gcc.dg/typedef-redecl2.c
index e69de29..c2314c7 100644
--- gcc/testsuite/gcc.dg/typedef-redecl2.c
+++ gcc/testsuite/gcc.dg/typedef-redecl2.c
@@ -0,0 +1,13 @@ 
+/* PR c/70297 */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#define N 64
+
+typedef int T;
+typedef int T __attribute__((aligned (N)));
+typedef int T __attribute__((aligned (N * 2)));
+typedef int T __attribute__((aligned (N)));
+typedef int T;
+
+_Static_assert (_Alignof (T) == N * 2, "N * 2");