aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/constexpr.c64
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C14
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C25
3 files changed, 90 insertions, 13 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index e0d3580..cb477c84 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4611,11 +4611,32 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2)
return same_type_ignoring_top_level_qualifiers_p (type1, type2);
}
+/* Try to determine the currently active union member for an expression
+ with UNION_TYPE. If it can be determined, return the FIELD_DECL,
+ otherwise return NULL_TREE. */
+
+static tree
+cxx_union_active_member (const constexpr_ctx *ctx, tree t)
+{
+ constexpr_ctx new_ctx = *ctx;
+ new_ctx.quiet = true;
+ bool non_constant_p = false, overflow_p = false;
+ tree ctor = cxx_eval_constant_expression (&new_ctx, t, false,
+ &non_constant_p,
+ &overflow_p);
+ if (TREE_CODE (ctor) == CONSTRUCTOR
+ && CONSTRUCTOR_NELTS (ctor) == 1
+ && CONSTRUCTOR_ELT (ctor, 0)->index
+ && TREE_CODE (CONSTRUCTOR_ELT (ctor, 0)->index) == FIELD_DECL)
+ return CONSTRUCTOR_ELT (ctor, 0)->index;
+ return NULL_TREE;
+}
+
/* Helper function for cxx_fold_indirect_ref_1, called recursively. */
static tree
-cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op,
- unsigned HOST_WIDE_INT off, bool *empty_base)
+cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
+ tree op, unsigned HOST_WIDE_INT off, bool *empty_base)
{
tree optype = TREE_TYPE (op);
unsigned HOST_WIDE_INT const_nunits;
@@ -4674,13 +4695,29 @@ cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op,
tree index = size_int (idx + tree_to_uhwi (min_val));
op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index,
NULL_TREE, NULL_TREE);
- return cxx_fold_indirect_ref_1 (loc, type, op, rem,
+ return cxx_fold_indirect_ref_1 (ctx, loc, type, op, rem,
empty_base);
}
}
/* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */
- else if (TREE_CODE (optype) == RECORD_TYPE)
+ else if (TREE_CODE (optype) == RECORD_TYPE
+ || TREE_CODE (optype) == UNION_TYPE)
{
+ if (TREE_CODE (optype) == UNION_TYPE)
+ /* For unions prefer the currently active member. */
+ if (tree field = cxx_union_active_member (ctx, op))
+ {
+ unsigned HOST_WIDE_INT el_sz
+ = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+ if (off < el_sz)
+ {
+ tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
+ op, field, NULL_TREE);
+ if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop,
+ off, empty_base))
+ return ret;
+ }
+ }
for (tree field = TYPE_FIELDS (optype);
field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
@@ -4691,13 +4728,13 @@ cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op,
if (!tree_fits_uhwi_p (pos))
continue;
unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos);
- unsigned el_sz
+ unsigned HOST_WIDE_INT el_sz
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
if (upos <= off && off < upos + el_sz)
{
tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
op, field, NULL_TREE);
- if (tree ret = cxx_fold_indirect_ref_1 (loc, type, cop,
+ if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop,
off - upos,
empty_base))
return ret;
@@ -4718,7 +4755,8 @@ cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op,
with TBAA in fold_indirect_ref_1. */
static tree
-cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
+cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
+ tree op0, bool *empty_base)
{
tree sub = op0;
tree subtype;
@@ -4756,7 +4794,7 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
return op;
}
else
- return cxx_fold_indirect_ref_1 (loc, type, op, 0, empty_base);
+ return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base);
}
else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
&& tree_fits_uhwi_p (TREE_OPERAND (sub, 1)))
@@ -4766,7 +4804,7 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
STRIP_NOPS (op00);
if (TREE_CODE (op00) == ADDR_EXPR)
- return cxx_fold_indirect_ref_1 (loc, type, TREE_OPERAND (op00, 0),
+ return cxx_fold_indirect_ref_1 (ctx, loc, type, TREE_OPERAND (op00, 0),
tree_to_uhwi (op01), empty_base);
}
/* *(foo *)fooarrptr => (*fooarrptr)[0] */
@@ -4776,7 +4814,7 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
tree type_domain;
tree min_val = size_zero_node;
tree newsub
- = cxx_fold_indirect_ref (loc, TREE_TYPE (subtype), sub, NULL);
+ = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL);
if (newsub)
sub = newsub;
else
@@ -4811,8 +4849,8 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
}
/* First try to simplify it directly. */
- tree r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), orig_op0,
- &empty_base);
+ tree r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t),
+ orig_op0, &empty_base);
if (!r)
{
/* If that didn't work, evaluate the operand first. */
@@ -4831,7 +4869,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
return t;
}
- r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0,
+ r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0,
&empty_base);
if (r == NULL_TREE)
{
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C
new file mode 100644
index 0000000..86b8aa9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C
@@ -0,0 +1,14 @@
+// PR c++/98122
+// { dg-do compile { target c++14 } }
+
+union U { int a; };
+
+constexpr bool
+foo ()
+{
+ U f { 42 };
+ constexpr auto m = &U::a;
+ return (f.*m) == 42;
+}
+
+static_assert (foo (), "");
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
new file mode 100644
index 0000000..01bdfa5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
@@ -0,0 +1,25 @@
+// PR c++/98122
+// { dg-do compile { target c++20 } }
+
+union V { int a; char b; };
+union W { int a; int b; };
+
+constexpr bool
+bar ()
+{
+ V f { .b = 42 };
+ constexpr auto m = &V::a;
+ return (f.*m) == 42;
+}
+
+constexpr bool
+baz ()
+{
+ W f { .b = 42 };
+ constexpr auto m = &W::b;
+ return (f.*m) == 42;
+}
+
+static_assert (bar (), ""); // { dg-error "non-constant condition for static assertion" }
+ // { dg-error "accessing 'V::a' member instead of initialized 'V::b' member in constant expression" "" { target *-*-* } .-1 }
+static_assert (baz (), "");