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

Reply via email to