Hi! 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.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2020-12-04 Jakub Jelinek <ja...@redhat.com> PR c++/98122 * constexpr.c (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. --- gcc/cp/constexpr.c.jj 2020-12-03 15:43:00.491620290 +0100 +++ gcc/cp/constexpr.c 2020-12-04 14:10:50.649944649 +0100 @@ -4614,8 +4614,8 @@ same_type_ignoring_tlq_and_bounds_p (tre /* 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 +4674,41 @@ cxx_fold_indirect_ref_1 (location_t loc, 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. */ + 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, op, 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) + { + tree field = CONSTRUCTOR_ELT (ctor, 0)->index; + 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 +4719,13 @@ cxx_fold_indirect_ref_1 (location_t loc, 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 +4746,8 @@ cxx_fold_indirect_ref_1 (location_t loc, 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 +4785,7 @@ cxx_fold_indirect_ref (location_t loc, t 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 +4795,7 @@ cxx_fold_indirect_ref (location_t loc, t 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 +4805,7 @@ cxx_fold_indirect_ref (location_t loc, t 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 +4840,8 @@ cxx_eval_indirect_ref (const constexpr_c } /* 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 +4860,7 @@ cxx_eval_indirect_ref (const constexpr_c 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) { --- gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C.jj 2020-12-04 14:19:58.919764864 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-98122.C 2020-12-04 14:20:33.097379640 +0100 @@ -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 (), ""); --- gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C.jj 2020-12-04 14:20:11.768620040 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C 2020-12-04 14:19:43.166942421 +0100 @@ -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 (), ""); Jakub