public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-5789] c++: Allow indeterminate unsigned char or std::byte in bit_cast - P1272R4
@ 2021-12-04 10:08 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2021-12-04 10:08 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:c57c910c945ac68ba9a7cda9b0f963173781d58c

commit r12-5789-gc57c910c945ac68ba9a7cda9b0f963173781d58c
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Sat Dec 4 11:02:15 2021 +0100

    c++: Allow indeterminate unsigned char or std::byte in bit_cast - P1272R4
    
    P1272R4 has added to the std::byteswap new stuff to me quite unrelated
    clarification for std::bit_cast.
    The patch treats it as DR, applying to all languages.
    We no longer diagnose if padding bits are stored into unsigned char
    or std::byte result, fields or bitfields, instead arrange for that result,
    those fields or bitfields to get indeterminate value (empty
    CONSTRUCTOR with CONSTRUCTOR_NO_ZEROING or just leaving the member's
    initializer out and setting CONSTRUCTOR_NO_ZEROING on parent).
    
    We still have a bug that we don't diagnose in lots of places lvalue-to-rvalue
    conversions of indeterminate values or class objects with some indeterminate
    members.
    
    2021-12-04  Jakub Jelinek <jakub@redhat.com>
    
            * cp-tree.h (is_byte_access_type_not_plain_char): Declare.
            * tree.c (is_byte_access_type_not_plain_char): New function.
            * constexpr.c (clear_uchar_or_std_byte_in_mask): New function.
            (cxx_eval_bit_cast): Don't error about padding bits if target
            type is unsigned char or std::byte, instead return no clearing
            ctor.  Use clear_uchar_or_std_byte_in_mask.
    
            * g++.dg/cpp2a/bit-cast11.C: New test.
            * g++.dg/cpp2a/bit-cast12.C: New test.
            * g++.dg/cpp2a/bit-cast13.C: New test.
            * g++.dg/cpp2a/bit-cast14.C: New test.

Diff:
---
 gcc/cp/constexpr.c                      | 131 +++++++++++++++++++++++++++++++-
 gcc/cp/cp-tree.h                        |   1 +
 gcc/cp/tree.c                           |  12 +++
 gcc/testsuite/g++.dg/cpp2a/bit-cast11.C |  63 +++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/bit-cast12.C |  68 +++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/bit-cast13.C |  63 +++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/bit-cast14.C |  75 ++++++++++++++++++
 7 files changed, 411 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index b4b8a96c6af..d851fae6681 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4275,6 +4275,118 @@ check_bit_cast_type (const constexpr_ctx *ctx, location_t loc, tree type,
   return false;
 }
 
+/* Helper function for cxx_eval_bit_cast.  For unsigned char or
+   std::byte members of CONSTRUCTOR (recursively) if they contain
+   some indeterminate bits (as set in MASK), remove the ctor elts,
+   mark the CONSTRUCTOR as CONSTRUCTOR_NO_CLEARING and clear the
+   bits in MASK.  */
+
+static void
+clear_uchar_or_std_byte_in_mask (location_t loc, tree t, unsigned char *mask)
+{
+  if (TREE_CODE (t) != CONSTRUCTOR)
+    return;
+
+  unsigned i, j = 0;
+  tree index, value;
+  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), i, index, value)
+    {
+      tree type = TREE_TYPE (value);
+      if (TREE_CODE (TREE_TYPE (t)) != ARRAY_TYPE
+	  && DECL_BIT_FIELD_TYPE (index) != NULL_TREE)
+	{
+	  if (is_byte_access_type_not_plain_char (DECL_BIT_FIELD_TYPE (index)))
+	    {
+	      HOST_WIDE_INT fldsz = TYPE_PRECISION (TREE_TYPE (index));
+	      gcc_assert (fldsz != 0);
+	      HOST_WIDE_INT pos = int_byte_position (index);
+	      HOST_WIDE_INT bpos
+		= tree_to_uhwi (DECL_FIELD_BIT_OFFSET (index));
+	      bpos %= BITS_PER_UNIT;
+	      HOST_WIDE_INT end
+		= ROUND_UP (bpos + fldsz, BITS_PER_UNIT) / BITS_PER_UNIT;
+	      gcc_assert (end == 1 || end == 2);
+	      unsigned char *p = mask + pos;
+	      unsigned char mask_save[2];
+	      mask_save[0] = mask[pos];
+	      mask_save[1] = end == 2 ? mask[pos + 1] : 0;
+	      if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+		sorry_at (loc, "PDP11 bit-field handling unsupported"
+			       " in %qs", "__builtin_bit_cast");
+	      else if (BYTES_BIG_ENDIAN)
+		{
+		  /* Big endian.  */
+		  if (bpos + fldsz <= BITS_PER_UNIT)
+		    *p &= ~(((1 << fldsz) - 1)
+			    << (BITS_PER_UNIT - bpos - fldsz));
+		  else
+		    {
+		      gcc_assert (bpos);
+		      *p &= ~(((1U << BITS_PER_UNIT) - 1) >> bpos);
+		      p++;
+		      fldsz -= BITS_PER_UNIT - bpos;
+		      gcc_assert (fldsz && fldsz < BITS_PER_UNIT);
+		      *p &= ((1U << BITS_PER_UNIT) - 1) >> fldsz;
+		    }
+		}
+	      else
+		{
+		  /* Little endian.  */
+		  if (bpos + fldsz <= BITS_PER_UNIT)
+		    *p &= ~(((1 << fldsz) - 1) << bpos);
+		  else
+		    {
+		      gcc_assert (bpos);
+		      *p &= ~(((1 << BITS_PER_UNIT) - 1) << bpos);
+		      p++;
+		      fldsz -= BITS_PER_UNIT - bpos;
+		      gcc_assert (fldsz && fldsz < BITS_PER_UNIT);
+		      *p &= ~((1 << fldsz) - 1);
+		    }
+		}
+	      if (mask_save[0] != mask[pos]
+		  || (end == 2 && mask_save[1] != mask[pos + 1]))
+		{
+		  CONSTRUCTOR_NO_CLEARING (t) = 1;
+		  continue;
+		}
+	    }
+	}
+      else if (is_byte_access_type_not_plain_char (type))
+	{
+	  HOST_WIDE_INT pos;
+	  if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+	    pos = tree_to_shwi (index);
+	  else
+	    pos = int_byte_position (index);
+	  if (mask[pos])
+	    {
+	      CONSTRUCTOR_NO_CLEARING (t) = 1;
+	      mask[pos] = 0;
+	      continue;
+	    }
+	}
+      if (TREE_CODE (value) == CONSTRUCTOR)
+	{
+	  HOST_WIDE_INT pos;
+	  if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+	    pos = tree_to_shwi (index)
+		  * tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t))));
+	  else
+	    pos = int_byte_position (index);
+	  clear_uchar_or_std_byte_in_mask (loc, value, mask + pos);
+	}
+      if (i != j)
+	{
+	  CONSTRUCTOR_ELT (t, j)->index = index;
+	  CONSTRUCTOR_ELT (t, j)->value = value;
+	}
+      ++j;
+    }
+  if (CONSTRUCTOR_NELTS (t) != j)
+    vec_safe_truncate (CONSTRUCTOR_ELTS (t), j);
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Attempt to evaluate a BIT_CAST_EXPR.  */
 
@@ -4351,12 +4463,27 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
 
   tree r = NULL_TREE;
   if (can_native_interpret_type_p (TREE_TYPE (t)))
-    r = native_interpret_expr (TREE_TYPE (t), ptr, len);
+    {
+      r = native_interpret_expr (TREE_TYPE (t), ptr, len);
+      if (is_byte_access_type_not_plain_char (TREE_TYPE (t)))
+	{
+	  gcc_assert (len == 1);
+	  if (mask[0])
+	    {
+	      memset (mask, 0, len);
+	      r = build_constructor (TREE_TYPE (r), NULL);
+	      CONSTRUCTOR_NO_CLEARING (r) = 1;
+	    }
+	}
+    }
   else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE)
     {
       r = native_interpret_aggregate (TREE_TYPE (t), ptr, 0, len);
       if (r != NULL_TREE)
-	clear_type_padding_in_mask (TREE_TYPE (t), mask);
+	{
+	  clear_type_padding_in_mask (TREE_TYPE (t), mask);
+	  clear_uchar_or_std_byte_in_mask (loc, r, mask);
+	}
     }
 
   if (r != NULL_TREE)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1ee2c57e83c..3510512d751 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7791,6 +7791,7 @@ extern tree build_dummy_object			(tree);
 extern tree maybe_dummy_object			(tree, tree *);
 extern bool is_dummy_object			(const_tree);
 extern bool is_byte_access_type			(tree);
+extern bool is_byte_access_type_not_plain_char	(tree);
 extern const struct attribute_spec cxx_attribute_table[];
 extern tree make_ptrmem_cst			(tree, tree);
 extern tree cp_build_type_attribute_variant     (tree, tree);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 1471ed8a2ef..52c56831f98 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4311,6 +4311,18 @@ is_byte_access_type (tree type)
 	  && !strcmp ("byte", TYPE_NAME_STRING (type)));
 }
 
+/* Returns true if TYPE is unsigned char or std::byte.  */
+
+bool
+is_byte_access_type_not_plain_char (tree type)
+{
+  type = TYPE_MAIN_VARIANT (type);
+  if (type == char_type_node)
+    return false;
+
+  return is_byte_access_type (type);
+}
+
 /* Returns 1 iff type T is something we want to treat as a scalar type for
    the purpose of deciding whether it is trivial/POD/standard-layout.  */
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C
new file mode 100644
index 00000000000..a3eb31bc6c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C
@@ -0,0 +1,63 @@
+// P1272R4
+// { dg-do compile { target c++14 } }
+
+struct S { unsigned char a[2]; alignas(sizeof 0) int b; };
+struct T { char a; alignas(sizeof 0) int b; };
+struct U { char a : 1; char : 6; char b : 1; };
+struct V { int a; S b; };
+struct W { unsigned a; T b; };
+
+constexpr bool
+f1 ()
+{
+  T t = { 1, 2 };
+  S s = __builtin_bit_cast (S, t);
+  return s.a[0] == 1;
+}
+
+constexpr bool
+f2 ()
+{
+  U u = { 0, 0 };
+  unsigned char a = __builtin_bit_cast (unsigned char, u);
+  return true;
+}
+
+constexpr bool
+f3 ()
+{
+  T t = { 1, 2 };
+  S s = __builtin_bit_cast (S, t);
+  return s.a[1] == 0;
+}
+
+constexpr bool
+f4 ()
+{
+  U u = { 0, 0 };
+  unsigned char a = __builtin_bit_cast (unsigned char, u);
+  return a == 0;		// { dg-error "is not a constant expression" }
+}
+
+constexpr bool
+f5 ()
+{
+  W t = { 1, 2 };
+  V s = __builtin_bit_cast (V, t);
+  return s.b.a[0] == 1;
+}
+
+constexpr bool
+f6 ()
+{
+  W t = { 1, 2 };
+  V s = __builtin_bit_cast (V, t);
+  return s.b.a[1] == 1;
+}
+
+constexpr bool a = f1 ();
+constexpr bool b = f2 ();
+constexpr bool c = f3 ();	// { dg-error "accessing uninitialized array element" }
+constexpr bool d = f4 ();
+constexpr bool e = f5 ();
+constexpr bool f = f6 ();	// { dg-error "accessing uninitialized array element" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C
new file mode 100644
index 00000000000..9c699dd55f0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C
@@ -0,0 +1,68 @@
+// P1272R4
+// { dg-do compile { target c++14 } }
+
+namespace std
+{
+  enum class byte : unsigned char {};
+}
+
+struct S { unsigned char a[2]; alignas(sizeof 0) int b; };
+struct T { char a; alignas(sizeof 0) int b; };
+struct U { char a : 1; char : 6; char b : 1; };
+struct V { int a; S b; };
+struct W { unsigned a; T b; };
+
+constexpr bool
+f1 ()
+{
+  T t = { 1, 2 };
+  S s = __builtin_bit_cast (S, t);
+  return s.a[0] == 1;
+}
+
+constexpr bool
+f2 ()
+{
+  U u = { 0, 0 };
+  unsigned char a = __builtin_bit_cast (unsigned char, u);
+  return true;
+}
+
+constexpr bool
+f3 ()
+{
+  T t = { 1, 2 };
+  S s = __builtin_bit_cast (S, t);
+  return s.a[1] == 0;
+}
+
+constexpr bool
+f4 ()
+{
+  U u = { 0, 0 };
+  unsigned char a = __builtin_bit_cast (unsigned char, u);
+  return a == 0;		// { dg-error "is not a constant expression" }
+}
+
+constexpr bool
+f5 ()
+{
+  W t = { 1, 2 };
+  V s = __builtin_bit_cast (V, t);
+  return s.b.a[0] == 1;
+}
+
+constexpr bool
+f6 ()
+{
+  W t = { 1, 2 };
+  V s = __builtin_bit_cast (V, t);
+  return s.b.a[1] == 1;
+}
+
+constexpr bool a = f1 ();
+constexpr bool b = f2 ();
+constexpr bool c = f3 ();	// { dg-error "accessing uninitialized array element" }
+constexpr bool d = f4 ();
+constexpr bool e = f5 ();
+constexpr bool f = f6 ();	// { dg-error "accessing uninitialized array element" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast13.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast13.C
new file mode 100644
index 00000000000..f3bae59241c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast13.C
@@ -0,0 +1,63 @@
+// P1272R4
+// { dg-do compile { target c++14 } }
+
+struct S { char a[2]; alignas(sizeof 0) int b; };
+struct T { char a; alignas(sizeof 0) int b; };
+struct U { char a : 1; char : 6; char b : 1; };
+struct V { int a; S b; };
+struct W { unsigned a; T b; };
+
+constexpr bool
+f1 ()
+{
+  T t = { 1, 2 };
+  S s = __builtin_bit_cast (S, t);	// { dg-error "accessing uninitialized byte" }
+  return s.a[0] == 1;
+}
+
+constexpr bool
+f2 ()
+{
+  U u = { 0, 0 };
+  char a = __builtin_bit_cast (char, u);	// { dg-error "accessing uninitialized byte" }
+  return true;
+}
+
+constexpr bool
+f3 ()
+{
+  T t = { 1, 2 };
+  S s = __builtin_bit_cast (S, t);	// { dg-error "accessing uninitialized byte" }
+  return s.a[1] == 0;
+}
+
+constexpr bool
+f4 ()
+{
+  U u = { 0, 0 };
+  char a = __builtin_bit_cast (char, u);	// { dg-error "accessing uninitialized byte" }
+  return a == 0;
+}
+
+constexpr bool
+f5 ()
+{
+  W t = { 1, 2 };
+  V s = __builtin_bit_cast (V, t);	// { dg-error "accessing uninitialized byte" }
+  return s.b.a[0] == 1;
+}
+
+constexpr bool
+f6 ()
+{
+  W t = { 1, 2 };
+  V s = __builtin_bit_cast (V, t);	// { dg-error "accessing uninitialized byte" }
+  return s.b.a[1] == 1;
+}
+
+constexpr bool a = f1 ();
+constexpr bool b = f2 ();
+constexpr bool c = f3 ();
+constexpr bool d = f4 ();
+constexpr bool e = f5 ();
+constexpr bool f = f6 ();
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C
new file mode 100644
index 00000000000..5e185919be4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C
@@ -0,0 +1,75 @@
+// P1272R4
+// { dg-do compile { target c++14 } }
+
+struct S { unsigned char a : 8, b : 5, c : 3, d, e; unsigned int f : 8, g : 24; };
+struct T1 { unsigned char a : 1, : 7, b : 5, c : 3, d, e; unsigned int f : 8, g : 24; };
+struct T2 { unsigned char a : 8, b : 1, : 4, c : 3, d, e; unsigned int f : 8, g : 24; };
+struct T3 { unsigned char a : 8, b : 5, c : 1, : 2, d, e; unsigned int f : 8, g : 24; };
+struct T4 { unsigned char a : 8, b : 5, c : 3, d, e; unsigned int f : 1, : 7, g : 24; };
+
+constexpr bool
+f1 ()
+{
+  T1 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);
+  return true;
+}
+
+constexpr bool
+f2 ()
+{
+  T2 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);
+  return true;
+}
+
+constexpr bool
+f3 ()
+{
+  T3 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);
+  return true;
+}
+
+constexpr bool
+f4 ()
+{
+  T4 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);	// { dg-error "accessing uninitialized byte" }
+  return true;
+}
+
+constexpr bool
+f5 ()
+{
+  T1 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);
+  unsigned char a = s.a;
+  return true;
+}
+
+constexpr bool
+f6 ()
+{
+  T2 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);
+  unsigned char b = s.b;
+  return true;
+}
+
+constexpr bool
+f7 ()
+{
+  T3 t = { 0, 0, 0, 0, 0, 0, 0 };
+  S s = __builtin_bit_cast (S, t);
+  unsigned char c = s.c;
+  return true;
+}
+
+constexpr bool a = f1 ();
+constexpr bool b = f2 ();
+constexpr bool c = f3 ();
+constexpr bool d = f4 ();
+constexpr bool e = f5 ();	// { dg-error "accessing uninitialized member" }
+constexpr bool f = f6 ();	// { dg-error "accessing uninitialized member" }
+constexpr bool g = f7 ();	// { dg-error "accessing uninitialized member" }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-12-04 10:08 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-04 10:08 [gcc r12-5789] c++: Allow indeterminate unsigned char or std::byte in bit_cast - P1272R4 Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).