public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-6918] c++: constexpr and empty fields [PR97566]
@ 2021-01-26 20:01 Jason Merrill
0 siblings, 0 replies; only message in thread
From: Jason Merrill @ 2021-01-26 20:01 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:a4dfd0f089af33f2af57bf422f9859405b9b4a16
commit r11-6918-ga4dfd0f089af33f2af57bf422f9859405b9b4a16
Author: Jason Merrill <jason@redhat.com>
Date: Sun Jan 24 00:55:49 2021 -0500
c++: constexpr and empty fields [PR97566]
In the discussion of PR98463, Jakub pointed out that in C++17 and up,
cxx_fold_indirect_ref_1 could use the field we build for an empty base. I
tried implementing that, but it broke one of the tuple tests, so I did some
more digging.
To start with, I generalized the PR98463 patch to handle the case where we
do have a field, for an empty base or [[no_unique_address]] member. This is
enough also for the no-field case because the member of the empty base must
itself be an empty field; if it weren't, the base would not be empty.
I looked for related PRs and found 97566, which was also fixed by the patch.
After some poking around to figure out why, I noticed that the testcase had
been breaking because E, though an empty class, has an ABI nvsize of one
byte, and we were giving the [[no_unique_address]] FIELD_DECL that
DECL_SIZE, whereas in build_base_field_1 empty base fields always get
DECL_SIZE zero, and various places were relying on that to recognize empty
fields. So I adjusted both the size and the checking. When I adjusted
check_bases I wondered if we were correctly handling bases with only empty
data members, but it appears we do.
I'm deferring the cxx_fold_indirect_ref_1 change until stage 1, as I don't
think it actually fixes anything.
gcc/cp/ChangeLog:
PR c++/97566
PR c++/98463
* class.c (layout_class_type): An empty field gets size 0.
(is_empty_field): New.
(check_bases): Check it.
* cp-tree.h (is_empty_field): Declare it.
* constexpr.c (cxx_eval_store_expression): Check it.
(cx_check_missing_mem_inits): Likewise.
* init.c (perform_member_init): Likewise.
* typeck2.c (process_init_constructor_record): Likewise.
gcc/testsuite/ChangeLog:
PR c++/97566
* g++.dg/cpp2a/no_unique_address10.C: New test.
* g++.dg/cpp2a/no_unique_address9.C: New test.
Diff:
---
gcc/cp/class.c | 31 ++++++++++++---
gcc/cp/constexpr.c | 20 ++++------
gcc/cp/cp-tree.h | 1 +
gcc/cp/init.c | 2 +-
gcc/cp/typeck2.c | 2 +-
gcc/testsuite/g++.dg/cpp2a/no_unique_address10.C | 16 ++++++++
gcc/testsuite/g++.dg/cpp2a/no_unique_address9.C | 50 ++++++++++++++++++++++++
7 files changed, 102 insertions(+), 20 deletions(-)
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 00c0dba0a55..40f5fef7baa 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -1835,15 +1835,13 @@ check_bases (tree t,
else if (CLASSTYPE_REPEATED_BASE_P (t))
CLASSTYPE_NON_STD_LAYOUT (t) = 1;
else
- /* ...either has no non-static data members in the most-derived
- class and at most one base class with non-static data
- members, or has no base classes with non-static data
- members. FIXME This was reworded in DR 1813. */
+ /* ...has all non-static data members and bit-fields in the class
+ and its base classes first declared in the same class. */
for (basefield = TYPE_FIELDS (basetype); basefield;
basefield = DECL_CHAIN (basefield))
if (TREE_CODE (basefield) == FIELD_DECL
&& !(DECL_FIELD_IS_BASE (basefield)
- && integer_zerop (DECL_SIZE (basefield))))
+ && is_empty_field (basefield)))
{
if (field)
CLASSTYPE_NON_STD_LAYOUT (t) = 1;
@@ -4226,6 +4224,25 @@ field_poverlapping_p (tree decl)
DECL_ATTRIBUTES (decl));
}
+/* Return true iff DECL is an empty field, either for an empty base or a
+ [[no_unique_address]] data member. */
+
+bool
+is_empty_field (tree decl)
+{
+ if (TREE_CODE (decl) != FIELD_DECL)
+ return false;
+
+ bool r = (is_empty_class (TREE_TYPE (decl))
+ && (DECL_FIELD_IS_BASE (decl)
+ || field_poverlapping_p (decl)));
+
+ /* Empty fields should have size zero. */
+ gcc_checking_assert (!r || integer_zerop (DECL_SIZE (decl)));
+
+ return r;
+}
+
/* Record all of the empty subobjects of DECL_OR_BINFO. */
static void
@@ -6612,7 +6629,9 @@ layout_class_type (tree t, tree *virtuals_p)
/* end_of_class doesn't always give dsize, but it does in the case of
a class with virtual bases, which is when dsize > nvsize. */
tree dsize = end_of_class (type, /*vbases*/true);
- if (tree_int_cst_le (dsize, nvsize))
+ if (CLASSTYPE_EMPTY_P (type))
+ DECL_SIZE (field) = DECL_SIZE_UNIT (field) = size_zero_node;
+ else if (tree_int_cst_le (dsize, nvsize))
{
DECL_SIZE_UNIT (field) = nvsize;
DECL_SIZE (field) = CLASSTYPE_SIZE (type);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index c1217535761..baa97a0ef17 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -821,7 +821,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
/* A flexible array can't be intialized here, so don't complain
that it isn't. */
continue;
- if (DECL_SIZE (field) && integer_zerop (DECL_SIZE (field)))
+ if (is_empty_field (field))
/* An empty field doesn't need an initializer. */
continue;
ftype = strip_array_types (ftype);
@@ -5291,17 +5291,13 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
type = refs->pop();
tree index = refs->pop();
- if (TREE_CODE (index) == FIELD_DECL
- && !(same_type_ignoring_top_level_qualifiers_p
- (DECL_CONTEXT (index), TREE_TYPE (*valp))))
- {
- /* INDEX isn't a member of *valp. This can happen if it's a member
- of an empty base which isn't represented with a FIELD_DECL. Stop
- trying to build a CONSTRUCTOR for the inner target; we'll notice
- this disconnect again below and just return init. */
- gcc_assert (is_empty_class (DECL_CONTEXT (index)));
- break;
- }
+ if (is_empty_field (index))
+ /* Don't build a sub-CONSTRUCTOR for an empty base or field, as they
+ have no data and might have an offset lower than previously declared
+ fields, which confuses the middle-end. The code below will notice
+ that we don't have a CONSTRUCTOR for our inner target and just
+ return init. */
+ break;
if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp)
&& CONSTRUCTOR_ELT (*valp, 0)->index != index)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index bef452f592a..f31319904eb 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6515,6 +6515,7 @@ extern int same_signature_p (const_tree, const_tree);
extern tree lookup_vfn_in_binfo (tree, tree);
extern void maybe_add_class_template_decl_list (tree, tree, int);
extern void unreverse_member_declarations (tree);
+extern bool is_empty_field (tree);
extern void invalidate_class_lookup_cache (void);
extern void maybe_note_name_used_in_class (tree, tree);
extern void note_name_declared_in_class (tree, tree);
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 3b37c970dfa..131da1a4ae4 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -877,7 +877,7 @@ perform_member_init (tree member, tree init)
}
if (init == error_mark_node)
return;
- if (DECL_SIZE (member) && integer_zerop (DECL_SIZE (member))
+ if (is_empty_field (member)
&& !TREE_SIDE_EFFECTS (init))
/* Don't add trivial initialization of an empty base/field, as they
might not be ordered the way the back-end expects. */
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index f4be72fc514..9ba2897390a 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1626,7 +1626,7 @@ process_init_constructor_record (tree type, tree init, int nested, int flags,
}
}
- if (DECL_SIZE (field) && integer_zerop (DECL_SIZE (field))
+ if (is_empty_field (field)
&& !TREE_SIDE_EFFECTS (next))
/* Don't add trivial initialization of an empty base/field to the
constructor, as they might not be ordered the way the back-end
diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address10.C b/gcc/testsuite/g++.dg/cpp2a/no_unique_address10.C
new file mode 100644
index 00000000000..cd9e8de4ad1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address10.C
@@ -0,0 +1,16 @@
+// Make sure [[no_unique_address]] doesn't affect is_standard_layout.
+// { dg-do compile { target c++11 } }
+
+struct E1 { }; struct E2 { };
+struct A
+{
+ [[no_unique_address]] E1 e;
+};
+
+struct B: A
+{
+ [[no_unique_address]] E2 e;
+};
+
+static_assert(__is_standard_layout (A), "");
+static_assert(!__is_standard_layout (B), "");
diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address9.C b/gcc/testsuite/g++.dg/cpp2a/no_unique_address9.C
new file mode 100644
index 00000000000..7837acf0ea5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address9.C
@@ -0,0 +1,50 @@
+// PR c++/97566
+// { dg-do compile { target c++14 } }
+
+// error disappears if E doesn't inherit from B
+struct B {};
+struct E : B {};
+
+struct counter {
+ constexpr void inc() { size++; }
+
+ // error disappears if you remove or reorder this value
+ int unused = 0;
+ int size = 0;
+ [[no_unique_address]] E empty = {};
+};
+
+#define SA(X) static_assert((X),#X)
+
+constexpr int test1() {
+ counter x;
+ x.inc();
+ return x.size;
+}
+SA(test1() == 1);
+
+constexpr int test2() {
+ counter x = { 0, 1, {} };
+ x.inc();
+ return x.size;
+}
+SA(test2() == 2);
+
+counter y;
+
+struct counter2 {
+ constexpr counter2() { inc(); }
+ constexpr void inc() { size++; }
+
+ // error disappears if you remove or reorder this value
+ int unused = 0;
+ int size = 0;
+ [[no_unique_address]] E empty = {};
+};
+
+constexpr int test3() {
+ counter2 x;
+ x.inc();
+ return x.size;
+}
+SA(test3() == 2);
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2021-01-26 20:01 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-26 20:01 [gcc r11-6918] c++: constexpr and empty fields [PR97566] Jason Merrill
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).