On Fri, Dec 03, 2021 at 01:56:56PM -0500, Jason Merrill wrote: > Ah, of course. The patch is OK, though you might factor > > > + else if (is_byte_access_type (type) > > + && TYPE_MAIN_VARIANT (type) != char_type_node) > > into is_byte_access_type_not_plain_char. OK either way.
Thanks, here is what I've committed in the end after another bootstrap/regtest. 2021-12-04 Jakub Jelinek <ja...@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. --- gcc/cp/cp-tree.h.jj 2021-11-26 10:10:53.216121541 +0100 +++ gcc/cp/cp-tree.h 2021-12-03 21:20:39.880277132 +0100 @@ -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); --- gcc/cp/tree.c.jj 2021-11-26 10:10:53.218121512 +0100 +++ gcc/cp/tree.c 2021-12-03 21:23:37.373738339 +0100 @@ -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. */ --- gcc/cp/constexpr.c.jj 2021-12-02 19:41:52.578553507 +0100 +++ gcc/cp/constexpr.c 2021-12-03 21:24:29.870987445 +0100 @@ -4275,6 +4275,118 @@ check_bit_cast_type (const constexpr_ctx 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 * 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) --- gcc/testsuite/g++.dg/cpp2a/bit-cast11.C.jj 2021-12-03 21:19:29.273286900 +0100 +++ gcc/testsuite/g++.dg/cpp2a/bit-cast11.C 2021-12-03 21:28:17.871726216 +0100 @@ -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" } --- gcc/testsuite/g++.dg/cpp2a/bit-cast12.C.jj 2021-12-03 21:19:29.273286900 +0100 +++ gcc/testsuite/g++.dg/cpp2a/bit-cast12.C 2021-12-03 21:28:41.353390350 +0100 @@ -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" } --- gcc/testsuite/g++.dg/cpp2a/bit-cast13.C.jj 2021-12-03 21:19:29.273286900 +0100 +++ gcc/testsuite/g++.dg/cpp2a/bit-cast13.C 2021-12-03 21:29:01.434103124 +0100 @@ -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 (); --- gcc/testsuite/g++.dg/cpp2a/bit-cast14.C.jj 2021-12-03 21:19:29.274286886 +0100 +++ gcc/testsuite/g++.dg/cpp2a/bit-cast14.C 2021-12-03 21:29:48.724426700 +0100 @@ -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" } Jakub