diff mbox series

c: don't drop typedef information in casts

Message ID 20210310012815.3797974-1-equinox@diac24.net
State New
Headers show
Series c: don't drop typedef information in casts | expand

Commit Message

David Lamparter March 10, 2021, 1:28 a.m. UTC
The TYPE_MAIN_VARIANT() here was, for casts to a typedef'd type name,
resulting in all information about the typedef's involvement getting
lost.  This drops necessary information for warnings and can make them
confusing or even misleading.  It also makes specialized warnings for
unspecified-size system types (pid_t, uid_t, ...) impossible.

gcc/c/ChangeLog:
2021-03-09  David Lamparter  <equinox@diac24.net>

        * c-typeck.c (build_c_cast): retain (unqualified) typedefs in
          casts rather than stripping down to basic type.
---
 gcc/c/c-typeck.c              | 11 +++++++----
 gcc/testsuite/gcc.dg/cast-5.c | 19 +++++++++++++++++++
 2 files changed, 26 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/cast-5.c

Comments

David Lamparter March 10, 2021, 1:46 a.m. UTC | #1
On Wed, Mar 10, 2021 at 02:28:15AM +0100, David Lamparter wrote:
> The TYPE_MAIN_VARIANT() here was, for casts to a typedef'd type name,
> resulting in all information about the typedef's involvement getting
> lost.  This drops necessary information for warnings and can make them
> confusing or even misleading.  It also makes specialized warnings for
> unspecified-size system types (pid_t, uid_t, ...) impossible.

I hope I didn't produce any glaring SNAFUs in submitting this patch, I
haven't previously contributed to GCC.

I've run the testsuite on x86_64 and seen no breakage from this.  The
delta (compared to this run:
https://gcc.gnu.org/pipermail/gcc-testresults/2021-March/662078.html) is:

$ diff -U0 /tmp/before.sum /tmp/after.sum
--- /tmp/before.sum     2021-03-09 22:48:26.989146223 +0100
+++ /tmp/after.sum      2021-03-10 01:46:46.209935875 +0100
@@ -89 +89,2 @@
-FAIL: g++.old-deja/g++.other/virtual2.C  -std=c++2a execution test
+FAIL: g++.dg/modules/xtreme-header-5_c.C -std=c++17 (internal compiler error)
+FAIL: g++.dg/modules/xtreme-header-5_c.C -std=c++17 (test for excess errors)

None of which seems to be related to the patch.  (The 2 new fails are
"xtreme-header-5_c.C:3:30: internal compiler error: same canonical type node for different types 'void' and 'std::__void_t<typename _Func::is_transparent>'")

Cheers,


David
Martin Sebor March 10, 2021, 5:01 p.m. UTC | #2
On 3/9/21 6:46 PM, David Lamparter wrote:
> On Wed, Mar 10, 2021 at 02:28:15AM +0100, David Lamparter wrote:
>> The TYPE_MAIN_VARIANT() here was, for casts to a typedef'd type name,
>> resulting in all information about the typedef's involvement getting
>> lost.  This drops necessary information for warnings and can make them
>> confusing or even misleading.  It also makes specialized warnings for
>> unspecified-size system types (pid_t, uid_t, ...) impossible.
> 
> I hope I didn't produce any glaring SNAFUs in submitting this patch, I
> haven't previously contributed to GCC.
> 
> I've run the testsuite on x86_64 and seen no breakage from this.  The
> delta (compared to this run:
> https://gcc.gnu.org/pipermail/gcc-testresults/2021-March/662078.html) is:

Thanks for the patch and for clarifying how you tested the change!

> 
> $ diff -U0 /tmp/before.sum /tmp/after.sum
> --- /tmp/before.sum     2021-03-09 22:48:26.989146223 +0100
> +++ /tmp/after.sum      2021-03-10 01:46:46.209935875 +0100
> @@ -89 +89,2 @@
> -FAIL: g++.old-deja/g++.other/virtual2.C  -std=c++2a execution test
> +FAIL: g++.dg/modules/xtreme-header-5_c.C -std=c++17 (internal compiler error)
> +FAIL: g++.dg/modules/xtreme-header-5_c.C -std=c++17 (test for excess errors)
> 
> None of which seems to be related to the patch.  (The 2 new fails are
> "xtreme-header-5_c.C:3:30: internal compiler error: same canonical type node for different types 'void' and 'std::__void_t<typename _Func::is_transparent>'")

Yes, these are unrelated C++ failures.

A few comments on the patch.  It took me a bit at first to see
the problem it tries to solve.  I see the improvement but I'm not
sure it's quite correct, or what in the test verifies the desired
outcome.  The warning on line 12 before the change is:

cast-5.c:13:7: warning: conversion from ‘int’ to ‘unsigned char’ may 
change value [-Wconversion]

and after:

cast-5.c:13:7: warning: conversion from ‘qualtypedefname’ {aka ‘int’} to 
‘unsigned char’ may change value [-Wconversion]

There's no volatile, so the test passes either way.  More important,
though, qualtypedefname is a typedef for volatile int but the AKA
type says it's int.  So the new output doesn't seem right.

In any case, the test should fail without the patch.  Using
dg-warning to look for both the expected typedef name and the AKA
type would seem like a better to do that than dg-bogus.

As an aside, although it's not a requirement, it's helpful to open
a bug first, before submitting a patch.  It's more overhead but it
gives the problem more visibility, lets us track regressions and
related bugs, and help decide what fixes should be backported.  In
this case, the same improvement can also be made to the C++ front
end and having a bug might help highlight that.

Finally, GCC is in a regression-fixing stage now, so unless this
problem is one (having a bug report would also help determine that),
this patch will likely need to wait until general development
reopens sometime in May.  In any case, it's a maintainer's call
to approve it.

Thanks again!

Martin
David Lamparter March 10, 2021, 7:02 p.m. UTC | #3
On Wed, Mar 10, 2021 at 10:01:39AM -0700, Martin Sebor wrote:
> On 3/9/21 6:46 PM, David Lamparter wrote:
> > On Wed, Mar 10, 2021 at 02:28:15AM +0100, David Lamparter wrote:
> >> The TYPE_MAIN_VARIANT() here was, for casts to a typedef'd type name,
> >> resulting in all information about the typedef's involvement getting
> >> lost.  This drops necessary information for warnings and can make them
> >> confusing or even misleading.  It also makes specialized warnings for
> >> unspecified-size system types (pid_t, uid_t, ...) impossible.

> A few comments on the patch.  It took me a bit at first to see
> the problem it tries to solve.  I see the improvement but I'm not
> sure it's quite correct, or what in the test verifies the desired
> outcome.

I've tried making it a bit more clear in a bug opened as requested:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99526

> The warning on line 12 before the change is:
> 
> cast-5.c:13:7: warning: conversion from ‘int’ to ‘unsigned char’ may 
> change value [-Wconversion]
> 
> and after:
> 
> cast-5.c:13:7: warning: conversion from ‘qualtypedefname’ {aka ‘int’} to 
> ‘unsigned char’ may change value [-Wconversion]
> 
> There's no volatile, so the test passes either way.  More important,
> though, qualtypedefname is a typedef for volatile int but the AKA
> type says it's int.  So the new output doesn't seem right.

The new output is actually correct since casts produce rvalues.  I was
trying to put together a test for that to make sure the volatile is NOT
there (in an earlier version it was, and that actually broke things.)

That said, I seem to have b0rked the test anyway since on closer look it
says: "ERROR: gcc.dg/cast-5.c: unknown dg option: */ for */"  (I had
missed that since I looked for PASS/FAIL...) - so need to fix that
anyway.

> In any case, the test should fail without the patch.  Using
> dg-warning to look for both the expected typedef name and the AKA
> type would seem like a better to do that than dg-bogus.

Thanks for the input, I need to look a bit more at how this whole stuff
works.  Going to poke it a bit more...

Also, in seeing your response re. the volatile, it occured to me that
stripping qualifiers on a typedef and still calling it the typedef is
unhelpful.  Ideally, my goal is:

typedef int i;
typedef const int ci;
typeof((i)x) = i
typeof((ci)x) = int  (<-- w/ my patch = ci; this is confusing)
typeof((const i)x) = i

(Without my patch, all 3 typeofs come out as plain int.)

So I think I need to somehow check whether the qualifiers are part of
the typedef or not;  essentially peel typedefs until it's one without
qualifiers.

> As an aside, although it's not a requirement, it's helpful to open
> a bug first, before submitting a patch. [...]

Linked above :)

> Finally, GCC is in a regression-fixing stage now, so unless this
> problem is one (having a bug report would also help determine that),
> this patch will likely need to wait until general development
> reopens sometime in May.  In any case, it's a maintainer's call
> to approve it.

Ah, I hadn't seen that.  My bad.


Thanks for the input!

-David
David Lamparter March 10, 2021, 7:06 p.m. UTC | #4
On Wed, Mar 10, 2021 at 08:02:16PM +0100, David Lamparter wrote:
> Also, in seeing your response re. the volatile, it occured to me that
> stripping qualifiers on a typedef and still calling it the typedef is
> unhelpful.  Ideally, my goal is:
> 
> typedef int i;
> typedef const int ci;
> typeof((i)x) = i
> typeof((ci)x) = int  (<-- w/ my patch = ci; this is confusing)

Clarification:  with the current patch, the type of (ci)x is *called*
"ci", but doesn't actually have the const qualifier.  That's the
confusing part.  The const (or volatile) is stripped off the typedef in
the added build_qualified_type() call, but that now-different type is
still referred to as "ci"...

> typeof((const i)x) = i
diff mbox series

Patch

diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 4e6d369c4c9c..0f67753be4d7 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -5816,6 +5816,7 @@  c_safe_function_type_cast_p (tree t1, tree t2)
 tree
 build_c_cast (location_t loc, tree type, tree expr)
 {
+  tree res_type;
   tree value;
 
   bool int_operands = EXPR_INT_CONST_OPERANDS (expr);
@@ -5836,7 +5837,9 @@  build_c_cast (location_t loc, tree type, tree expr)
   if (objc_is_object_ptr (type) && objc_is_object_ptr (TREE_TYPE (expr)))
     return build1 (NOP_EXPR, type, expr);
 
-  type = TYPE_MAIN_VARIANT (type);
+  /* rvalue - retain typedefs w/o qualifier, but use actual type for checks */
+  res_type = build_qualified_type (type, TYPE_UNQUALIFIED);
+  type = TYPE_MAIN_VARIANT (res_type);
 
   if (TREE_CODE (type) == ARRAY_TYPE)
     {
@@ -5864,7 +5867,7 @@  build_c_cast (location_t loc, tree type, tree expr)
 		 "ISO C forbids casting nonscalar to the same type");
 
       /* Convert to remove any qualifiers from VALUE's type.  */
-      value = convert (type, value);
+      value = convert (res_type, value);
     }
   else if (TREE_CODE (type) == UNION_TYPE)
     {
@@ -6018,7 +6021,7 @@  build_c_cast (location_t loc, tree type, tree expr)
 		    " from %qT to %qT", otype, type);
 
       ovalue = value;
-      value = convert (type, value);
+      value = convert (res_type, value);
 
       /* Ignore any integer overflow caused by the cast.  */
       if (TREE_CODE (value) == INTEGER_CST && !FLOAT_TYPE_P (otype))
@@ -6054,7 +6057,7 @@  build_c_cast (location_t loc, tree type, tree expr)
 		&& INTEGRAL_TYPE_P (TREE_TYPE (expr)))
 	       || TREE_CODE (expr) == REAL_CST
 	       || TREE_CODE (expr) == COMPLEX_CST)))
-      value = build1 (NOP_EXPR, type, value);
+      value = build1 (NOP_EXPR, res_type, value);
 
   /* If the expression has integer operands and so can occur in an
      unevaluated part of an integer constant expression, ensure the
diff --git a/gcc/testsuite/gcc.dg/cast-5.c b/gcc/testsuite/gcc.dg/cast-5.c
new file mode 100644
index 000000000000..dcd9b290653d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cast-5.c
@@ -0,0 +1,19 @@ 
+/* Test cast <> typedef interactions */
+/* Origin: David Lamparter <equinox@diac24.net> */
+/* { dg-do compile } */
+/* { dg-options "-Wconversion" } */
+
+typedef int typedefname;
+typedef volatile int qualtypedefname;
+
+extern int val;
+extern void f2(unsigned char arg);
+
+void
+f (void)
+{
+  /* -Wconversion just used to print out the actual type */
+
+  f2 ((typedefname) val); /* { dg-warning "typedefname" } */
+  f2 ((qualtypedefname) val); /* { dg-warning "qualtypedefname" } */ /* { dg-bogus "volatile" } */
+}