c++: Fix for decltype and bit-fields [PR95009]

Message ID 20211204202617.362235-1-polacek@redhat.com
State Committed
Commit 3a2257e6b3fa288d6c50831987949b9ff7dfb865
Headers
Series c++: Fix for decltype and bit-fields [PR95009] |

Commit Message

Marek Polacek Dec. 4, 2021, 8:26 p.m. UTC
  Here, decltype deduces the wrong type for certain expressions involving
bit-fields.  Unlike in C, in C++ bit-field width is explicitly not part
of the type, so I think decltype should never deduce to 'int:N'.  The
problem isn't that we're not calling unlowered_expr_type--we are--it's
that is_bitfield_expr_with_lowered_type only handles certain codes, but
not others.  For example, += works fine but ++ does not.

This also fixes decltype-bitfield2.C where we were crashing (!), but
unfortunately it does not fix 84516 or 70733 where the problem is likely
a missing call to unlowered_expr_type.  It occurs to me now that typeof
likely has had the same issue, but this patch should fix that too.

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

	PR c++/95009

gcc/cp/ChangeLog:

	* typeck.c (is_bitfield_expr_with_lowered_type) <case MODIFY_EXPR>:
	Handle UNARY_PLUS_EXPR, NEGATE_EXPR, NON_LVALUE_EXPR, BIT_NOT_EXPR,
	*CREMENT_EXPR too.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/decltype-bitfield1.C: New test.
	* g++.dg/cpp0x/decltype-bitfield2.C: New test.
---
 gcc/cp/typeck.c                               | 14 +++-
 .../g++.dg/cpp0x/decltype-bitfield1.C         | 65 +++++++++++++++++++
 .../g++.dg/cpp0x/decltype-bitfield2.C         | 18 +++++
 3 files changed, 94 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C


base-commit: 689407ef916503b2f5a3c8c07fe7d5ab1913f956
  

Comments

Jason Merrill Dec. 6, 2021, 9:35 p.m. UTC | #1
On 12/4/21 15:26, Marek Polacek wrote:
> Here, decltype deduces the wrong type for certain expressions involving
> bit-fields.  Unlike in C, in C++ bit-field width is explicitly not part
> of the type, so I think decltype should never deduce to 'int:N'.  The
> problem isn't that we're not calling unlowered_expr_type--we are--it's
> that is_bitfield_expr_with_lowered_type only handles certain codes, but
> not others.  For example, += works fine but ++ does not.
> 
> This also fixes decltype-bitfield2.C where we were crashing (!), but
> unfortunately it does not fix 84516 or 70733 where the problem is likely
> a missing call to unlowered_expr_type.  It occurs to me now that typeof
> likely has had the same issue, but this patch should fix that too.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/11?

OK.

> 	PR c++/95009
> 
> gcc/cp/ChangeLog:
> 
> 	* typeck.c (is_bitfield_expr_with_lowered_type) <case MODIFY_EXPR>:
> 	Handle UNARY_PLUS_EXPR, NEGATE_EXPR, NON_LVALUE_EXPR, BIT_NOT_EXPR,
> 	*CREMENT_EXPR too.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/decltype-bitfield1.C: New test.
> 	* g++.dg/cpp0x/decltype-bitfield2.C: New test.
> ---
>   gcc/cp/typeck.c                               | 14 +++-
>   .../g++.dg/cpp0x/decltype-bitfield1.C         | 65 +++++++++++++++++++
>   .../g++.dg/cpp0x/decltype-bitfield2.C         | 18 +++++
>   3 files changed, 94 insertions(+), 3 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C
> 
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 5ed9a5ab9ee..4e60db40c76 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -2209,9 +2209,9 @@ invalid_nonstatic_memfn_p (location_t loc, tree expr, tsubst_flags_t complain)
>     return false;
>   }
>   
> -/* If EXP is a reference to a bitfield, and the type of EXP does not
> -   match the declared type of the bitfield, return the declared type
> -   of the bitfield.  Otherwise, return NULL_TREE.  */
> +/* If EXP is a reference to a bit-field, and the type of EXP does not
> +   match the declared type of the bit-field, return the declared type
> +   of the bit-field.  Otherwise, return NULL_TREE.  */
>   
>   tree
>   is_bitfield_expr_with_lowered_type (const_tree exp)
> @@ -2230,6 +2230,14 @@ is_bitfield_expr_with_lowered_type (const_tree exp)
>   
>       case MODIFY_EXPR:
>       case SAVE_EXPR:
> +    case UNARY_PLUS_EXPR:
> +    case PREDECREMENT_EXPR:
> +    case PREINCREMENT_EXPR:
> +    case POSTDECREMENT_EXPR:
> +    case POSTINCREMENT_EXPR:
> +    case NEGATE_EXPR:
> +    case NON_LVALUE_EXPR:
> +    case BIT_NOT_EXPR:
>         return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 0));
>   
>       case COMPONENT_REF:
> diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C
> new file mode 100644
> index 00000000000..2d8d8e81bff
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C
> @@ -0,0 +1,65 @@
> +// PR c++/95009
> +// { dg-do compile { target c++11 } }
> +
> +struct false_type { static constexpr bool value = false; };
> +struct true_type { static constexpr bool value = true; };
> +template<class T, class U>
> +struct is_same : false_type {};
> +template<class T>
> +struct is_same<T, T> : true_type {};
> +
> +struct A {
> +  int i : 31;
> +  unsigned long l : 37;
> +} a;
> +
> +void
> +g ()
> +{
> +  // Careful: pre{in,de}crements are lvalues -> deduce T&.  */
> +  static_assert (is_same<decltype(a.i), int>::value, "");
> +  static_assert (is_same<decltype((a.i)), int&>::value, "");
> +  static_assert (is_same<decltype(++a.i), int&>::value, "");
> +  static_assert (is_same<decltype((++a.i)), int&>::value, "");
> +  static_assert (is_same<decltype(a.i++), int>::value, "");
> +  static_assert (is_same<decltype((a.i++)), int>::value, "");
> +  static_assert (is_same<decltype(--a.i), int&>::value, "");
> +  static_assert (is_same<decltype((--a.i)), int&>::value, "");
> +  static_assert (is_same<decltype(a.i--), int>::value, "");
> +  static_assert (is_same<decltype((a.i--)), int>::value, "");
> +  static_assert (is_same<decltype(a.i += 1), int&>::value, "");
> +  static_assert (is_same<decltype((a.i += 1)), int&>::value, "");
> +  static_assert (is_same<decltype(a.i -= 1), int&>::value, "");
> +  static_assert (is_same<decltype((a.i -= 1)), int&>::value, "");
> +  static_assert (is_same<decltype(a.i *= 1), int&>::value, "");
> +  static_assert (is_same<decltype((a.i *= 1)), int&>::value, "");
> +  static_assert (is_same<decltype(+a.i), int>::value, "");
> +  static_assert (is_same<decltype((+a.i)), int>::value, "");
> +  static_assert (is_same<decltype(-a.i), int>::value, "");
> +  static_assert (is_same<decltype((-a.i)), int>::value, "");
> +  static_assert (is_same<decltype(~a.i), int>::value, "");
> +  static_assert (is_same<decltype((~a.i)), int>::value, "");
> +
> +  static_assert (is_same<decltype(a.l), unsigned long>::value, "");
> +  static_assert (is_same<decltype((a.l)), unsigned long&>::value, "");
> +  static_assert (is_same<decltype(++a.l), unsigned long&>::value, "");
> +  static_assert (is_same<decltype((++a.l)), unsigned long&>::value, "");
> +  static_assert (is_same<decltype(a.l++), unsigned long>::value, "");
> +  static_assert (is_same<decltype((a.l++)), unsigned long>::value, "");
> +  static_assert (is_same<decltype(--a.l), unsigned long&>::value, "");
> +  static_assert (is_same<decltype((--a.l)), unsigned long&>::value, "");
> +  static_assert (is_same<decltype(a.l--), unsigned long>::value, "");
> +  static_assert (is_same<decltype((a.l--)), unsigned long>::value, "");
> +  static_assert (is_same<decltype(a.l += 1), unsigned long&>::value, "");
> +  static_assert (is_same<decltype((a.l += 1)), unsigned long&>::value, "");
> +  static_assert (is_same<decltype(a.l -= 1), unsigned long&>::value, "");
> +  static_assert (is_same<decltype((a.l -= 1)), unsigned long&>::value, "");
> +  static_assert (is_same<decltype(a.l *= 1), unsigned long&>::value, "");
> +  static_assert (is_same<decltype((a.l *= 1)), unsigned long&>::value, "");
> +  static_assert (is_same<decltype(+a.l), unsigned long>::value, "");
> +  static_assert (is_same<decltype((+a.l)), unsigned long>::value, "");
> +  static_assert (is_same<decltype(-a.l), unsigned long>::value, "");
> +  static_assert (is_same<decltype((-a.l)), unsigned long>::value, "");
> +  static_assert (is_same<decltype(~a.l), unsigned long>::value, "");
> +  static_assert (is_same<decltype((~a.l)), unsigned long>::value, "");
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C
> new file mode 100644
> index 00000000000..4bf9c7682ff
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C
> @@ -0,0 +1,18 @@
> +// PR c++/95009
> +// { dg-do compile { target c++11 } }
> +
> +struct A {
> +  int i:31;
> +};
> +
> +template<typename>
> +void f ()
> +{
> +}
> +
> +int main ()
> +{
> +  A a;
> +  f<decltype(a.i += 1)>();
> +  f<decltype(++a.i)>();
> +}
> 
> base-commit: 689407ef916503b2f5a3c8c07fe7d5ab1913f956
>
  

Patch

diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 5ed9a5ab9ee..4e60db40c76 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2209,9 +2209,9 @@  invalid_nonstatic_memfn_p (location_t loc, tree expr, tsubst_flags_t complain)
   return false;
 }
 
-/* If EXP is a reference to a bitfield, and the type of EXP does not
-   match the declared type of the bitfield, return the declared type
-   of the bitfield.  Otherwise, return NULL_TREE.  */
+/* If EXP is a reference to a bit-field, and the type of EXP does not
+   match the declared type of the bit-field, return the declared type
+   of the bit-field.  Otherwise, return NULL_TREE.  */
 
 tree
 is_bitfield_expr_with_lowered_type (const_tree exp)
@@ -2230,6 +2230,14 @@  is_bitfield_expr_with_lowered_type (const_tree exp)
 
     case MODIFY_EXPR:
     case SAVE_EXPR:
+    case UNARY_PLUS_EXPR:
+    case PREDECREMENT_EXPR:
+    case PREINCREMENT_EXPR:
+    case POSTDECREMENT_EXPR:
+    case POSTINCREMENT_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case BIT_NOT_EXPR:
       return is_bitfield_expr_with_lowered_type (TREE_OPERAND (exp, 0));
 
     case COMPONENT_REF:
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C
new file mode 100644
index 00000000000..2d8d8e81bff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield1.C
@@ -0,0 +1,65 @@ 
+// PR c++/95009
+// { dg-do compile { target c++11 } }
+
+struct false_type { static constexpr bool value = false; };
+struct true_type { static constexpr bool value = true; };
+template<class T, class U>
+struct is_same : false_type {}; 
+template<class T>
+struct is_same<T, T> : true_type {};
+
+struct A {
+  int i : 31;
+  unsigned long l : 37;
+} a;
+
+void
+g ()
+{
+  // Careful: pre{in,de}crements are lvalues -> deduce T&.  */
+  static_assert (is_same<decltype(a.i), int>::value, "");
+  static_assert (is_same<decltype((a.i)), int&>::value, "");
+  static_assert (is_same<decltype(++a.i), int&>::value, "");
+  static_assert (is_same<decltype((++a.i)), int&>::value, "");
+  static_assert (is_same<decltype(a.i++), int>::value, "");
+  static_assert (is_same<decltype((a.i++)), int>::value, "");
+  static_assert (is_same<decltype(--a.i), int&>::value, "");
+  static_assert (is_same<decltype((--a.i)), int&>::value, "");
+  static_assert (is_same<decltype(a.i--), int>::value, "");
+  static_assert (is_same<decltype((a.i--)), int>::value, "");
+  static_assert (is_same<decltype(a.i += 1), int&>::value, "");
+  static_assert (is_same<decltype((a.i += 1)), int&>::value, "");
+  static_assert (is_same<decltype(a.i -= 1), int&>::value, "");
+  static_assert (is_same<decltype((a.i -= 1)), int&>::value, "");
+  static_assert (is_same<decltype(a.i *= 1), int&>::value, "");
+  static_assert (is_same<decltype((a.i *= 1)), int&>::value, "");
+  static_assert (is_same<decltype(+a.i), int>::value, "");
+  static_assert (is_same<decltype((+a.i)), int>::value, "");
+  static_assert (is_same<decltype(-a.i), int>::value, "");
+  static_assert (is_same<decltype((-a.i)), int>::value, "");
+  static_assert (is_same<decltype(~a.i), int>::value, "");
+  static_assert (is_same<decltype((~a.i)), int>::value, "");
+
+  static_assert (is_same<decltype(a.l), unsigned long>::value, "");
+  static_assert (is_same<decltype((a.l)), unsigned long&>::value, "");
+  static_assert (is_same<decltype(++a.l), unsigned long&>::value, "");
+  static_assert (is_same<decltype((++a.l)), unsigned long&>::value, "");
+  static_assert (is_same<decltype(a.l++), unsigned long>::value, "");
+  static_assert (is_same<decltype((a.l++)), unsigned long>::value, "");
+  static_assert (is_same<decltype(--a.l), unsigned long&>::value, "");
+  static_assert (is_same<decltype((--a.l)), unsigned long&>::value, "");
+  static_assert (is_same<decltype(a.l--), unsigned long>::value, "");
+  static_assert (is_same<decltype((a.l--)), unsigned long>::value, "");
+  static_assert (is_same<decltype(a.l += 1), unsigned long&>::value, "");
+  static_assert (is_same<decltype((a.l += 1)), unsigned long&>::value, "");
+  static_assert (is_same<decltype(a.l -= 1), unsigned long&>::value, "");
+  static_assert (is_same<decltype((a.l -= 1)), unsigned long&>::value, "");
+  static_assert (is_same<decltype(a.l *= 1), unsigned long&>::value, "");
+  static_assert (is_same<decltype((a.l *= 1)), unsigned long&>::value, "");
+  static_assert (is_same<decltype(+a.l), unsigned long>::value, "");
+  static_assert (is_same<decltype((+a.l)), unsigned long>::value, "");
+  static_assert (is_same<decltype(-a.l), unsigned long>::value, "");
+  static_assert (is_same<decltype((-a.l)), unsigned long>::value, "");
+  static_assert (is_same<decltype(~a.l), unsigned long>::value, "");
+  static_assert (is_same<decltype((~a.l)), unsigned long>::value, "");
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C
new file mode 100644
index 00000000000..4bf9c7682ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype-bitfield2.C
@@ -0,0 +1,18 @@ 
+// PR c++/95009
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int i:31;
+};
+
+template<typename>
+void f ()
+{ 
+}
+
+int main ()
+{
+  A a;
+  f<decltype(a.i += 1)>();
+  f<decltype(++a.i)>();
+}