Use enclosing object size if it's smaller than member [PR 101475].

Resolves:
PR middle-end/101475 - missing -Wstringop-overflow storing a compound literal

gcc/ChangeLog:

	PR middle-end/101475
	* pointer-query.cc (handle_component_ref): Use the size of
	the enclosing object if it's smaller than the member.

gcc/testsuite/ChangeLog:

	PR middle-end/101475
	* gcc.dg/Wstringop-overflow-68.c: Adjust, remove xfails.
	* gcc.dg/Wstringop-overflow-88.c: New test.

diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 4bedf7fca47..644b4de9bcb 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -1914,36 +1914,40 @@ handle_component_ref (tree cref, gimple *stmt, bool addr, int ostype,
   gcc_assert (TREE_CODE (cref) == COMPONENT_REF);
 
   const tree base = TREE_OPERAND (cref, 0);
+  const tree field = TREE_OPERAND (cref, 1);
+  access_ref base_ref = *pref;
+
+  /* Unconditionally determine the size of the base object (it could
+     be smaller than the referenced member).  */
+  if (!compute_objsize_r (base, stmt, addr, 0, &base_ref, snlim, qry))
+    return false;
+
+  /* Add the offset of the member to the offset into the object computed
+     so far.  */
+  tree offset = byte_position (field);
+  if (TREE_CODE (offset) == INTEGER_CST)
+    base_ref.add_offset (wi::to_offset (offset));
+  else
+    base_ref.add_max_offset ();
+
+  if (!base_ref.ref)
+    /* PREF->REF may have been already set to an SSA_NAME earlier
+       to provide better context for diagnostics.  In that case,
+       leave it unchanged.  */
+    base_ref.ref = base;
+
   const tree base_type = TREE_TYPE (base);
   if (TREE_CODE (base_type) == UNION_TYPE)
     /* In accesses through union types consider the entire unions
        rather than just their members.  */
     ostype = 0;
 
-  tree field = TREE_OPERAND (cref, 1);
-
   if (ostype == 0)
     {
       /* In OSTYPE zero (for raw memory functions like memcpy), use
 	 the maximum size instead if the identity of the enclosing
 	 object cannot be determined.  */
-      if (!compute_objsize_r (base, stmt, addr, ostype, pref, snlim, qry))
-	return false;
-
-      /* Otherwise, use the size of the enclosing object and add
-	 the offset of the member to the offset computed so far.  */
-      tree offset = byte_position (field);
-      if (TREE_CODE (offset) == INTEGER_CST)
-	pref->add_offset (wi::to_offset (offset));
-      else
-	pref->add_max_offset ();
-
-      if (!pref->ref)
-	/* PREF->REF may have been already set to an SSA_NAME earlier
-	   to provide better context for diagnostics.  In that case,
-	   leave it unchanged.  */
-	pref->ref = base;
-
+      *pref = base_ref;
       return true;
     }
 
@@ -1958,6 +1962,11 @@ handle_component_ref (tree cref, gimple *stmt, bool addr, int ostype,
     }
 
   set_component_ref_size (cref, pref);
+
+  if (base_ref.size_remaining () < pref->size_remaining ())
+    /* Use the base object if it's smaller than the member.  */
+    *pref = base_ref;
+
   return true;
 }
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c
index 05ea56fca67..4d132394f0f 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-68.c
@@ -2,7 +2,7 @@
    a larger scalar into a smaller array
    Verify overflow by aggregate stores.
    { dg-do compile }
-   { dg-options "-O2" } */
+   { dg-options "-O2 -fno-tree-vectorize" } */
 
 #define A(N) (A ## N)
 #define Ac1 (AC1){ 0 }
@@ -57,19 +57,20 @@ void warn_comp_lit_zero (void)
 
 void warn_comp_lit (void)
 {
-  *(AC2*)a1 = Ac2;      // { dg-warning "writing 2 bytes into a region of size 1" "pr101475" { xfail *-*-* } }
-  // After vectorization, below codes are optimized to
-  // MEM <vector(4) char> [(char *)&a2] = { 0, 1, 2, 3 };
-  // MEM <vector(4) char> [(char *)&a3] = { 0, 1, 2, 3 };
-  // MEM <vector(8) char> [(char *)&a4] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-  // MEM <vector(8) char> [(char *)&a7] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-  // MEM <vector(16) char> [(char *)&a15] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
-  // and warning should be expected, refer to PR102722.
-  *(AC4*)a2 = Ac4;      // { dg-warning "writing 4 bytes into a region of size 2" "pr101475" { xfail { ! { vect_slp_v4qi_store_unalign_1 } } } }
-  *(AC4*)a3 = Ac4;      // { dg-warning "writing 4 bytes into a region of size 3" "pr101475" { xfail { ! { vect_slp_v4qi_store_unalign_1 } } } }
-  *(AC8*)a4 = Ac8;      // { dg-warning "writing 8 bytes into a region of size 4" "pr101475" { xfail { ! { vect_slp_v8qi_store_unalign_1 } } } }
-  *(AC8*)a7 = Ac8;      // { dg-warning "writing 8 bytes into a region of size 7" "pr101475" { xfail { ! { vect_slp_v8qi_store_unalign_1 } } } }
-  *(AC16*)a15 = Ac16;   // { dg-warning "writing 16 bytes into a region of size 15" "pr101475" { xfail { ! { vect_slp_v16qi_store_unalign_1 } } } }
+  /* Ideally only one warning would be issued for each of the stores
+     mentioning the size of the rest of the source being assigned to
+     the destination that doesn't fit.  But without vectorization
+     the assignment is a series of one-character stores, except in
+     the first instance multiple warnings end up being issued for
+     each assignment, each saying "writing 1 byte into a region of
+     size 0".  That's suboptimal and should be improved.  See also
+     PR 92110.  */
+  *(AC2*)a1 = Ac2;      // { dg-warning "writing (2 bytes|1 byte) into a region of size (1|0)" "pr101475" }
+  *(AC4*)a2 = Ac4;      // { dg-warning "writing (4 bytes|1 byte) into a region of size (2|0)" "pr101475" }
+  *(AC4*)a3 = Ac4;      // { dg-warning "writing (4 bytes|1 byte) into a region of size (3|0)" "pr101475" }
+  *(AC8*)a4 = Ac8;      // { dg-warning "writing (8 bytes|1 byte) into a region of size (4|0)" "pr101475" }
+  *(AC8*)a7 = Ac8;      // { dg-warning "writing (8 bytes|1 byte) into a region of size (7|0)" "pr101475" }
+  *(AC16*)a15 = Ac16;   // { dg-warning "writing (16 bytes|1 byte) into a region of size (15|0)" "pr101475" }
 }
 
 void warn_aggr_decl (void)
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-88.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-88.c
new file mode 100644
index 00000000000..c6b443e4d3d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-88.c
@@ -0,0 +1,327 @@
+/* PR middle-end/101475 - missing -Wstringop-overflow storing a compound
+   literal
+   { dg-do compile }
+   { dg-options "-O2 -fno-tree-vectorize" } */
+
+extern char ea1[1], ea2[2], ea3[3], ea4[4];
+
+/* The trailing A member of all of Sx, S0, and S1 is treated the same:
+   as a flexible array member.  */
+struct Sx { char n, a[]; };
+struct S0 { char n, a[0]; };
+struct S1 { char n, a[1]; };
+/* The trailing A member in both S2 and S3 is treated as an ordinary
+   array with exactly two elements and accesses to elements beyond
+   the last are diagnosed regardless of whether they are within
+   the bounds the enclosing object.  */
+struct S2 { char n, a[2]; };
+struct S3 { char n, a[3]; };
+
+
+void fx_ea1 (void)
+{
+  struct Sx *p = (struct Sx*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea1 (void)
+{
+  struct S0 *p = (struct S0*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea1 (void)
+{
+  struct S1 *p = (struct S1*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea1 (void)
+{
+  struct S2 *p = (struct S2*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea1 (void)
+{
+  struct S3 *p = (struct S3*)ea1;
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea1_p1 (void)
+{
+  struct Sx *p = (struct Sx*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea1_p1 (void)
+{
+  struct S0 *p = (struct S0*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea1_p1 (void)
+{
+  struct S1 *p = (struct S1*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea1_p1 (void)
+{
+  struct S2 *p = (struct S2*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea1_p1 (void)
+{
+  struct S3 *p = (struct S3*)(ea1 + 1);
+  p->n = 0;         // { dg-warning "-Wstringop-overflow" }
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea2 (void)
+{
+  struct Sx *p = (struct Sx*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea2 (void)
+{
+  struct S0 *p = (struct S0*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea2 (void)
+{
+  struct S1 *p = (struct S1*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea2 (void)
+{
+  struct S2 *p = (struct S2*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea2 (void)
+{
+  struct S3 *p = (struct S3*)ea2;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea2_p1 (void)
+{
+  struct Sx *p = (struct Sx*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" } 
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea2_p1 (void)
+{
+  struct S0 *p = (struct S0*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea2_p1 (void)
+{
+  struct S1 *p = (struct S1*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea2_p1 (void)
+{
+  struct S2 *p = (struct S2*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea2_p1 (void)
+{
+  struct S3 *p = (struct S3*)(ea2 + 1);
+  p->n = 0;
+  p->a[0] = 0;      // { dg-warning "-Wstringop-overflow" }
+  p->a[1] = 1;      // { dg-warning "-Wstringop-overflow" }
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea3 (void)
+{
+  struct Sx *p = (struct Sx*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea3 (void)
+{
+  struct S0 *p = (struct S0*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea3 (void)
+{
+  struct S1 *p = (struct S1*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea3 (void)
+{
+  struct S2 *p = (struct S2*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea3 (void)
+{
+  struct S3 *p = (struct S3*)ea3;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void fx_ea4 (void)
+{
+  struct Sx *p = (struct Sx*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f0_ea4 (void)
+{
+  struct S0 *p = (struct S0*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f1_ea4 (void)
+{
+  struct S1 *p = (struct S1*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f2_ea4 (void)
+{
+  struct S2 *p = (struct S2*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  /* Even though the offset of p->a[2] is within the bounds of EA4
+     the warning triggers because it only considers trailing arrays
+     of at mnost one element as "poor man's flexible arrays."  */
+  p->a[2] = 2;      // { dg-warning "-Wstringop-overflow" }
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
+
+void f3_ea4 (void)
+{
+  struct S3 *p = (struct S3*)ea4;
+  p->n = 0;
+  p->a[0] = 0;
+  p->a[1] = 1;
+  p->a[2] = 2;
+  p->a[3] = 3;      // { dg-warning "-Wstringop-overflow" }
+}
