Hi!

The C++ testcase shows we aren't able to determine constant when loading
from
const union U { struct V { int a, b; } c; long long d; } u = { { 1, 2 } };
u.d, but since your patch in the summer can handle
const union V { int a[2]; long long d; } v = { { 1, 2 } };
v.d.

I have noticed dwarf2out.c already has native_encode_initializer that can
deal with extracting stuff from CONSTRUCTORs (recursively), the following
patch just moves it over next to native_encode_expr and adjusts it to match
the native_encode_expr argument standards, i.e. ptr == NULL for dry-run,
off -1 when the whole object needs to be extracted and fitted into ptr
through ptr + len - 1, and then other off values to extract base + off
up to base + off + len - 1 bytes from the ctor into ptr up to ptr + len - 1
and added to the toplevel fold_ctor_reference this native_encode_initializer
as fallback if other ways failed.  E.g. we can't ATM extract RECORD_TYPEs
etc. through native_interpret_expr, while fold_{,non}array_ctor_reference
can extract them, but only if it doesn't overlap multiple initializers.
I;ve done it in the toplevel fold_ctor_reference, not recursive calls,
as I think that would imply exponential compile time behavior.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2020-01-10  Jakub Jelinek  <ja...@redhat.com>

        PR tree-optimization/93210
        * fold-const.h (native_encode_initializer,
        can_native_interpret_type_p): Declare.
        * fold-const.c (native_encode_string): Fix up handling with off != -1,
        simplify.
        (native_encode_initializer): New function, moved from dwarf2out.c.
        Adjust to native_encode_expr compatible arguments, including dry-run
        and partial extraction modes.  Don't handle STRING_CST.
        (can_native_interpret_type_p): No longer static.
        * gimple-fold.c (fold_ctor_reference): For native_encode_expr, verify
        offset / BITS_PER_UNIT fits into int and don't call it if
        can_native_interpret_type_p fails.  If suboff is NULL and for
        CONSTRUCTOR fold_{,non}array_ctor_reference returns NULL, retry with
        native_encode_initializer.
        (fold_const_aggregate_ref_1): Formatting fix.
        * dwarf2out.c (native_encode_initializer): Moved to fold-const.c.
        (tree_add_const_value_attribute): Adjust caller.

        * gcc.dg/pr93210.c: New test.
        * g++.dg/opt/pr93210.C: New test.

--- gcc/fold-const.h.jj 2020-01-01 12:15:53.773522669 +0100
+++ gcc/fold-const.h    2020-01-10 11:33:37.516681975 +0100
@@ -26,7 +26,10 @@ extern int folding_initializer;
 
 /* Convert between trees and native memory representation.  */
 extern int native_encode_expr (const_tree, unsigned char *, int, int off = -1);
+extern int native_encode_initializer (tree, unsigned char *, int,
+                                     int off = -1);
 extern tree native_interpret_expr (tree, const unsigned char *, int);
+extern bool can_native_interpret_type_p (tree);
 
 /* Fold constants as much as possible in an expression.
    Returns the simplified expression.
--- gcc/fold-const.c.jj 2020-01-01 12:15:52.894535959 +0100
+++ gcc/fold-const.c    2020-01-10 12:47:13.637310535 +0100
@@ -7837,9 +7837,10 @@ native_encode_string (const_tree expr, u
     return 0;
   if (off == -1)
     off = 0;
+  len = MIN (total_bytes - off, len);
   if (ptr == NULL)
     /* Dry run.  */;
-  else if (TREE_STRING_LENGTH (expr) - off < MIN (total_bytes, len))
+  else
     {
       int written = 0;
       if (off < TREE_STRING_LENGTH (expr))
@@ -7847,12 +7848,9 @@ native_encode_string (const_tree expr, u
          written = MIN (len, TREE_STRING_LENGTH (expr) - off);
          memcpy (ptr, TREE_STRING_POINTER (expr) + off, written);
        }
-      memset (ptr + written, 0,
-             MIN (total_bytes - written, len - written));
+      memset (ptr + written, 0, len - written);
     }
-  else
-    memcpy (ptr, TREE_STRING_POINTER (expr) + off, MIN (total_bytes, len));
-  return MIN (total_bytes - off, len);
+  return len;
 }
 
 
@@ -7895,6 +7893,213 @@ native_encode_expr (const_tree expr, uns
     }
 }
 
+/* Similar to native_encode_expr, but also handle CONSTRUCTORs, VCEs,
+   NON_LVALUE_EXPRs and nops.  */
+
+int
+native_encode_initializer (tree init, unsigned char *ptr, int len,
+                          int off)
+{
+  /* We don't support starting at negative offset and -1 is special.  */
+  if (off < -1 || init == NULL_TREE)
+    return 0;
+
+  STRIP_NOPS (init);
+  switch (TREE_CODE (init))
+    {
+    case VIEW_CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+      return native_encode_initializer (TREE_OPERAND (init, 0), ptr, len, off);
+    default:
+      return native_encode_expr (init, ptr, len, off);
+    case CONSTRUCTOR:
+      tree type = TREE_TYPE (init);
+      HOST_WIDE_INT total_bytes = int_size_in_bytes (type);
+      if (total_bytes < 0)
+       return 0;
+      if ((off == -1 && total_bytes > len) || off >= total_bytes)
+       return 0;
+      int o = off == -1 ? 0 : off;
+      if (TREE_CODE (type) == ARRAY_TYPE)
+       {
+         HOST_WIDE_INT min_index;
+         unsigned HOST_WIDE_INT cnt;
+         HOST_WIDE_INT curpos = 0, fieldsize;
+         constructor_elt *ce;
+
+         if (TYPE_DOMAIN (type) == NULL_TREE
+             || !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
+           return 0;
+
+         fieldsize = int_size_in_bytes (TREE_TYPE (type));
+         if (fieldsize <= 0)
+           return 0;
+
+         min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type)));
+         if (ptr != NULL)
+           memset (ptr, '\0', MIN (total_bytes - off, len));
+
+         FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
+           {
+             tree val = ce->value;
+             tree index = ce->index;
+             HOST_WIDE_INT pos = curpos, count = 0;
+             bool full = false;
+             if (index && TREE_CODE (index) == RANGE_EXPR)
+               {
+                 if (!tree_fits_shwi_p (TREE_OPERAND (index, 0))
+                     || !tree_fits_shwi_p (TREE_OPERAND (index, 1)))
+                   return 0;
+                 pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index)
+                       * fieldsize;
+                 count = (tree_to_shwi (TREE_OPERAND (index, 1))
+                          - tree_to_shwi (TREE_OPERAND (index, 0)));
+               }
+             else if (index)
+               {
+                 if (!tree_fits_shwi_p (index))
+                   return 0;
+                 pos = (tree_to_shwi (index) - min_index) * fieldsize;
+               }
+
+             curpos = pos;
+             if (val)
+               do
+                 {
+                   if (off == -1
+                       || (curpos >= off
+                           && (curpos + fieldsize
+                               <= (HOST_WIDE_INT) off + len)))
+                     {
+                       if (full)
+                         {
+                           if (ptr)
+                             memcpy (ptr + (curpos - o), ptr + (pos - o),
+                                     fieldsize);
+                         }
+                       else if (!native_encode_initializer (val,
+                                                            ptr
+                                                            ? ptr + curpos - o
+                                                            : NULL,
+                                                            fieldsize,
+                                                            off == -1 ? -1
+                                                                      : 0))
+                         return 0;
+                       else
+                         {
+                           full = true;
+                           pos = curpos;
+                         }
+                     }
+                   else if (curpos + fieldsize > off
+                            && curpos < (HOST_WIDE_INT) off + len)
+                     {
+                       /* Partial overlap.  */
+                       unsigned char *p = NULL;
+                       int no = 0;
+                       int l;
+                       if (curpos >= off)
+                         {
+                           if (ptr)
+                             p = ptr + curpos - off;
+                           l = MIN ((HOST_WIDE_INT) off + len - curpos,
+                                    fieldsize);
+                         }
+                       else
+                         {
+                           p = ptr;
+                           no = off - curpos;
+                           l = len;
+                         }
+                       if (!native_encode_initializer (val, p, l, no))
+                         return 0;
+                     }
+                   curpos += fieldsize;
+                 }
+               while (count-- != 0);
+           }
+         return MIN (total_bytes - off, len);
+       }
+      else if (TREE_CODE (type) == RECORD_TYPE
+              || TREE_CODE (type) == UNION_TYPE)
+       {
+         unsigned HOST_WIDE_INT cnt;
+         constructor_elt *ce;
+
+         if (ptr != NULL)
+           memset (ptr, '\0', MIN (total_bytes - off, len));
+         FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
+           {
+             tree field = ce->index;
+             tree val = ce->value;
+             HOST_WIDE_INT pos, fieldsize;
+
+             if (field == NULL_TREE)
+               return 0;
+
+             pos = int_byte_position (field);
+             if (off != -1 && (HOST_WIDE_INT) off + len <= pos)
+               continue;
+
+             if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
+                 && TYPE_DOMAIN (TREE_TYPE (field))
+                 && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field))))
+               return 0;
+             if (DECL_SIZE_UNIT (field) == NULL_TREE
+                 || !tree_fits_shwi_p (DECL_SIZE_UNIT (field)))
+               return 0;
+             fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field));
+             if (fieldsize == 0)
+               continue;
+
+             if (off != -1 && pos + fieldsize <= off)
+               continue;
+
+             if (DECL_BIT_FIELD (field))
+               return 0;
+
+             if (val == NULL_TREE)
+               continue;
+
+             if (off == -1
+                 || (pos >= off
+                     && (pos + fieldsize <= (HOST_WIDE_INT) off + len)))
+               {
+                 if (!native_encode_initializer (val, ptr ? ptr + pos - o
+                                                          : NULL,
+                                                 fieldsize,
+                                                 off == -1 ? -1 : 0))
+                   return 0;
+               }
+             else
+               {
+                 /* Partial overlap.  */
+                 unsigned char *p = NULL;
+                 int no = 0;
+                 int l;
+                 if (pos >= off)
+                   {
+                     if (ptr)
+                       p = ptr + pos - off;
+                     l = MIN ((HOST_WIDE_INT) off + len - pos,
+                               fieldsize);
+                   }
+                 else
+                   {
+                     p = ptr;
+                     no = off - pos;
+                     l = len;
+                   }
+                 if (!native_encode_initializer (val, p, l, no))
+                   return 0;
+               }
+           }
+         return MIN (total_bytes - off, len);
+       }
+      return 0;
+    }
+}
+
 
 /* Subroutine of native_interpret_expr.  Interpret the contents of
    the buffer PTR of length LEN as an INTEGER_CST of type TYPE.
@@ -8129,7 +8334,7 @@ native_interpret_expr (tree type, const
 /* Returns true if we can interpret the contents of a native encoding
    as TYPE.  */
 
-static bool
+bool
 can_native_interpret_type_p (tree type)
 {
   switch (TREE_CODE (type))
--- gcc/gimple-fold.c.jj        2020-01-08 16:54:04.781286448 +0100
+++ gcc/gimple-fold.c   2020-01-10 11:23:54.071459431 +0100
@@ -6919,8 +6919,10 @@ fold_ctor_reference (tree type, tree cto
   if (CONSTANT_CLASS_P (ctor)
       && BITS_PER_UNIT == 8
       && offset % BITS_PER_UNIT == 0
+      && offset / BITS_PER_UNIT <= INT_MAX
       && size % BITS_PER_UNIT == 0
-      && size <= MAX_BITSIZE_MODE_ANY_MODE)
+      && size <= MAX_BITSIZE_MODE_ANY_MODE
+      && can_native_interpret_type_p (type))
     {
       unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
       int len = native_encode_expr (ctor, buf, size / BITS_PER_UNIT,
@@ -6934,13 +6936,35 @@ fold_ctor_reference (tree type, tree cto
       if (!suboff)
        suboff = &dummy;
 
+      tree ret;
       if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE
          || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE)
-       return fold_array_ctor_reference (type, ctor, offset, size,
-                                         from_decl, suboff);
+       ret = fold_array_ctor_reference (type, ctor, offset, size,
+                                        from_decl, suboff);
+      else
+       ret = fold_nonarray_ctor_reference (type, ctor, offset, size,
+                                           from_decl, suboff);
+
+      /* Fall back to native_encode_initializer.  Needs to be done
+        only in the outermost fold_ctor_reference call (because it itself
+        recurses into CONSTRUCTORs) and doesn't update suboff.  */
+      if (ret == NULL_TREE
+         && suboff == &dummy
+         && BITS_PER_UNIT == 8
+         && offset % BITS_PER_UNIT == 0
+         && offset / BITS_PER_UNIT <= INT_MAX
+         && size % BITS_PER_UNIT == 0
+         && size <= MAX_BITSIZE_MODE_ANY_MODE
+         && can_native_interpret_type_p (type))
+       {
+         unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
+         int len = native_encode_initializer (ctor, buf, size / BITS_PER_UNIT,
+                                              offset / BITS_PER_UNIT);
+         if (len > 0)
+           return native_interpret_expr (type, buf, len);
+       }
 
-      return fold_nonarray_ctor_reference (type, ctor, offset, size,
-                                          from_decl, suboff);
+      return ret;
     }
 
   return NULL_TREE;
@@ -7049,7 +7073,7 @@ fold_const_aggregate_ref_1 (tree t, tree
        tree c = fold_const_aggregate_ref_1 (TREE_OPERAND (t, 0), valueize);
        if (c && TREE_CODE (c) == COMPLEX_CST)
          return fold_build1_loc (EXPR_LOCATION (t),
-                             TREE_CODE (t), TREE_TYPE (t), c);
+                                 TREE_CODE (t), TREE_TYPE (t), c);
        break;
       }
 
--- gcc/dwarf2out.c.jj  2020-01-01 12:15:43.000000000 +0100
+++ gcc/dwarf2out.c     2020-01-10 11:06:03.370522471 +0100
@@ -20258,150 +20258,6 @@ add_location_or_const_value_attribute (d
   return tree_add_const_value_attribute_for_decl (die, decl);
 }
 
-/* Helper function for tree_add_const_value_attribute.  Natively encode
-   initializer INIT into an array.  Return true if successful.  */
-
-static bool
-native_encode_initializer (tree init, unsigned char *array, int size)
-{
-  tree type;
-
-  if (init == NULL_TREE)
-    return false;
-
-  STRIP_NOPS (init);
-  switch (TREE_CODE (init))
-    {
-    case STRING_CST:
-      type = TREE_TYPE (init);
-      if (TREE_CODE (type) == ARRAY_TYPE)
-       {
-         tree enttype = TREE_TYPE (type);
-         scalar_int_mode mode;
-
-         if (!is_int_mode (TYPE_MODE (enttype), &mode)
-             || GET_MODE_SIZE (mode) != 1)
-           return false;
-         if (int_size_in_bytes (type) != size)
-           return false;
-         if (size > TREE_STRING_LENGTH (init))
-           {
-             memcpy (array, TREE_STRING_POINTER (init),
-                     TREE_STRING_LENGTH (init));
-             memset (array + TREE_STRING_LENGTH (init),
-                     '\0', size - TREE_STRING_LENGTH (init));
-           }
-         else
-           memcpy (array, TREE_STRING_POINTER (init), size);
-         return true;
-       }
-      return false;
-    case CONSTRUCTOR:
-      type = TREE_TYPE (init);
-      if (int_size_in_bytes (type) != size)
-       return false;
-      if (TREE_CODE (type) == ARRAY_TYPE)
-       {
-         HOST_WIDE_INT min_index;
-         unsigned HOST_WIDE_INT cnt;
-         int curpos = 0, fieldsize;
-         constructor_elt *ce;
-
-         if (TYPE_DOMAIN (type) == NULL_TREE
-             || !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
-           return false;
-
-         fieldsize = int_size_in_bytes (TREE_TYPE (type));
-         if (fieldsize <= 0)
-           return false;
-
-         min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type)));
-         memset (array, '\0', size);
-         FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
-           {
-             tree val = ce->value;
-             tree index = ce->index;
-             int pos = curpos;
-             if (index && TREE_CODE (index) == RANGE_EXPR)
-               pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index)
-                     * fieldsize;
-             else if (index)
-               pos = (tree_to_shwi (index) - min_index) * fieldsize;
-
-             if (val)
-               {
-                 STRIP_NOPS (val);
-                 if (!native_encode_initializer (val, array + pos, fieldsize))
-                   return false;
-               }
-             curpos = pos + fieldsize;
-             if (index && TREE_CODE (index) == RANGE_EXPR)
-               {
-                 int count = tree_to_shwi (TREE_OPERAND (index, 1))
-                             - tree_to_shwi (TREE_OPERAND (index, 0));
-                 while (count-- > 0)
-                   {
-                     if (val)
-                       memcpy (array + curpos, array + pos, fieldsize);
-                     curpos += fieldsize;
-                   }
-               }
-             gcc_assert (curpos <= size);
-           }
-         return true;
-       }
-      else if (TREE_CODE (type) == RECORD_TYPE
-              || TREE_CODE (type) == UNION_TYPE)
-       {
-         tree field = NULL_TREE;
-         unsigned HOST_WIDE_INT cnt;
-         constructor_elt *ce;
-
-         if (int_size_in_bytes (type) != size)
-           return false;
-
-         if (TREE_CODE (type) == RECORD_TYPE)
-           field = TYPE_FIELDS (type);
-
-         FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce)
-           {
-             tree val = ce->value;
-             int pos, fieldsize;
-
-             if (ce->index != 0)
-               field = ce->index;
-
-             if (val)
-               STRIP_NOPS (val);
-
-             if (field == NULL_TREE || DECL_BIT_FIELD (field))
-               return false;
-
-             if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
-                 && TYPE_DOMAIN (TREE_TYPE (field))
-                 && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field))))
-               return false;
-             else if (DECL_SIZE_UNIT (field) == NULL_TREE
-                      || !tree_fits_shwi_p (DECL_SIZE_UNIT (field)))
-               return false;
-             fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field));
-             pos = int_byte_position (field);
-             gcc_assert (pos + fieldsize <= size);
-             if (val && fieldsize != 0
-                 && !native_encode_initializer (val, array + pos, fieldsize))
-               return false;
-           }
-         return true;
-       }
-      return false;
-    case VIEW_CONVERT_EXPR:
-    case NON_LVALUE_EXPR:
-      return native_encode_initializer (TREE_OPERAND (init, 0), array, size);
-    default:
-      return native_encode_expr (init, array, size) == size;
-    }
-}
-
 /* Attach a DW_AT_const_value attribute to DIE. The value of the
    attribute is the const value T.  */
 
@@ -20446,7 +20302,7 @@ tree_add_const_value_attribute (dw_die_r
        {
          unsigned char *array = ggc_cleared_vec_alloc<unsigned char> (size);
 
-         if (native_encode_initializer (init, array, size))
+         if (native_encode_initializer (init, array, size) == size)
            {
              add_AT_vec (die, DW_AT_const_value, size, 1, array);
              return true;
--- gcc/testsuite/gcc.dg/pr93210.c.jj   2020-01-10 13:03:25.901743750 +0100
+++ gcc/testsuite/gcc.dg/pr93210.c      2020-01-10 13:01:21.567607632 +0100
@@ -0,0 +1,66 @@
+/* PR tree-optimization/93210 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times "return \[0-9]\[0-9a-fA-FxX]*;" 31 
"optimized" } } */
+
+#ifdef __SIZEOF_INT128__
+typedef unsigned __int128 L;
+#else
+typedef unsigned long long L;
+#endif
+struct S { signed char a, b; unsigned char c; };
+struct T { signed char d; struct S e[25]; signed char f; };
+union U { struct T g; L h[10]; };
+const union U u = { { 1, { { 2, 3, 4 }, { 5, 6, 7 }, { 8, 9, 10 },
+                           { 12, 13, 14 }, { 15, 16, 17 }, { 18, 19, 20 },
+                           { 22, 23, 24 }, { 25, 26, 27 }, { 28, 29, 30 },
+                           { 32, 33, 34 }, { 35, 36, 37 }, { 38, 39, 40 },
+                           { 42, 43, 44 }, { 45, 46, 47 }, { 48, 49, 50 },
+                           { 52, 53, 54 }, { 55, 56, 57 }, { 58, 59, 60 },
+                           { 62, 63, 64 }, { 65, 66, 67 }, { 68, 69, 70 },
+                           { 72, 73, 74 }, { 75, 76, 77 }, { 78, 79, 80 },
+                           { 82, 83, 84 } }, 85 } };
+const union U v = { { 1, { { 2, 3, 4 }, [1 ... 23] = { 5, 6, 7 },
+                          { 8, 9, 10 } }, 86 } };
+struct A { char a[5]; char b[16]; char c[7]; };
+union V { struct A d; unsigned int e[10]; };
+const union V w = { { "abcde", "ijkl", "mnopqr" } };
+#define N(n) __attribute__((noipa)) L foo##n (void) { return u.h[n]; }
+#define M N(0) N(1) N(2) N(3) N(4) N(5) N(6) N(7) N(8) N(9)
+M
+#undef N
+#define N(n) __attribute__((noipa)) L bar##n (void) { return v.h[n]; }
+M
+#undef N
+#define N(n) __attribute__((noipa)) L baz##n (void) { return w.e[n]; }
+M
+
+typedef L (*F) (void);
+F arr[30] = {
+#undef N
+#define N(n) foo##n,
+M
+#undef N
+#define N(n) bar##n,
+M
+#undef N
+#define N(n) baz##n,
+M
+};
+
+int
+main ()
+{
+  const union U *p = &u;
+  const union U *q = &v;
+  const union V *r = &w;
+  __asm ("" : "+g" (p));
+  __asm ("" : "+g" (q));
+  __asm ("" : "+g" (r));
+  for (int i = 0; i < 10; i++)
+    if (arr[i] () != p->h[i]
+       || arr[i + 10] () != q->h[i]
+       || arr[i + 20] () != r->e[i])
+      __builtin_abort ();
+  return 0;
+}
--- gcc/testsuite/g++.dg/opt/pr93210.C.jj       2020-01-10 13:11:57.065082378 
+0100
+++ gcc/testsuite/g++.dg/opt/pr93210.C  2020-01-10 13:13:53.138342895 +0100
@@ -0,0 +1,37 @@
+// PR tree-optimization/93210
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -fdump-tree-optimized" }
+// { dg-final { scan-tree-dump-not "static_member\.d" "optimized" } }
+
+union U { struct { unsigned int a, b; } c; unsigned long long d; };
+
+inline
+bool operator == (U const &x, U const &y) noexcept
+{
+  return x.d == y.d;
+};
+
+struct S
+{
+  static constexpr U static_member = { { 13, 42 } };
+  bool foo (U const &y) const noexcept;
+  bool bar (U const &y) const noexcept;
+};
+
+#if __cpp_inline_variables < 201606L
+constexpr U S::static_member;
+#endif
+
+#if __SIZEOF_INT__ * 2 == __SIZEOF_LONG_LONG__
+bool
+S::foo (U const &y) const noexcept
+{
+  return static_member == y;
+}
+
+bool
+S::bar (U const &y) const noexcept
+{
+  return U (static_member) == y;
+}
+#endif

        Jakub

Reply via email to