mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-06 02:20:34 +08:00
c++: Fix constexpr access to union member through pointer-to-member [PR98122]
We currently incorrectly reject the first testcase, because cxx_fold_indirect_ref_1 doesn't attempt to handle UNION_TYPEs. As the second testcase shows, it isn't that easy, because I believe we need to take into account the active member and prefer that active member over other members, because if we pick a non-active one, we might reject valid programs. 2020-12-05 Jakub Jelinek <jakub@redhat.com> PR c++/98122 * constexpr.c (cxx_union_active_member): New function. (cxx_fold_indirect_ref_1): Add ctx argument, pass it through to recursive call. Handle UNION_TYPE. (cxx_fold_indirect_ref): Add ctx argument, pass it to recursive calls and cxx_fold_indirect_ref_1. (cxx_eval_indirect_ref): Adjust cxx_fold_indirect_ref calls. * g++.dg/cpp1y/constexpr-98122.C: New test. * g++.dg/cpp2a/constexpr-98122.C: New test.
This commit is contained in:
parent
c5fd8a9157
commit
43e84ce7d6
@ -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)
|
||||
{
|
||||
|
14
gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C
Normal file
14
gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C
Normal file
@ -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 (), "");
|
25
gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
Normal file
25
gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
Normal file
@ -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 (), "");
|
Loading…
x
Reference in New Issue
Block a user