diff --git a/gcc/testsuite/gcc.dg/Wzero-length-array-bounds-3.c b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..9292d182e37bd2463b085ab77e487c8592215d33
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds-3.c
@@ -0,0 +1,19 @@
+/* PR tree-optimization/109215 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall" } */
+
+struct S {};
+struct T { struct S s[3]; struct S t; };
+void foo (struct S *);
+
+void
+bar (struct T *t)
+{
+  foo (&t->s[2]);	/* { dg-bogus "array subscript 2 is outside the bounds of an interior zero-length array" } */
+}
+
+void
+baz (struct T *t)
+{
+  foo (&t->s[3]);	/* { dg-error "" "" { xfail *-*-* } } */
+}
diff --git a/gcc/tree.cc b/gcc/tree.cc
index d17106e99a814f5bd787b4fbda81de7a91e81af4..207293c48cba64c973d379779e69159eefdb1088 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -13032,14 +13032,27 @@ component_ref_sam_type (tree ref)
 	return sam_type;
 
       bool trailing = false;
-      (void)array_ref_flexible_size_p (ref, &trailing);
-      bool zero_length = integer_zerop (memsize);
-      if (!trailing && !zero_length)
-	/* MEMBER is an interior array with
-	  more than one element.  */
+      (void) array_ref_flexible_size_p (ref, &trailing);
+      bool zero_elts = integer_zerop (memsize);
+      if (zero_elts && integer_zerop (TYPE_SIZE_UNIT (TREE_TYPE (memtype))))
+	{
+	  /* If array element has zero size, verify if it is a flexible
+	     array member or zero length array.  Clear zero_elts if
+	     it has one or more members or is a VLA member.  */
+	  if (tree dom = TYPE_DOMAIN (memtype))
+	    if (tree min = TYPE_MIN_VALUE (dom))
+	      if (tree max = TYPE_MAX_VALUE (dom))
+		if (TREE_CODE (min) != INTEGER_CST
+		    || TREE_CODE (max) != INTEGER_CST
+		    || !((integer_zerop (min) && integer_all_onesp (max))
+			 || tree_int_cst_lt (max, min)))
+		  zero_elts = false;
+	}
+      if (!trailing && !zero_elts)
+	/* MEMBER is an interior array with more than one element.  */
 	return special_array_member::int_n;
 
-      if (zero_length)
+      if (zero_elts)
 	{
 	  if (trailing)
 	    return special_array_member::trail_0;
@@ -13047,7 +13060,7 @@ component_ref_sam_type (tree ref)
 	    return special_array_member::int_0;
 	}
 
-      if (!zero_length)
+      if (!zero_elts)
 	if (tree dom = TYPE_DOMAIN (memtype))
 	  if (tree min = TYPE_MIN_VALUE (dom))
 	    if (tree max = TYPE_MAX_VALUE (dom))
@@ -13114,14 +13127,14 @@ component_ref_size (tree ref, special_array_member *sam /* = NULL */)
 
       tree afield_decl = TREE_OPERAND (ref, 1);
       gcc_assert (TREE_CODE (afield_decl) == FIELD_DECL);
-      /* if the trailing array is a not a flexible array member, treat it as
+      /* If the trailing array is a not a flexible array member, treat it as
 	 a normal array.  */
       if (DECL_NOT_FLEXARRAY (afield_decl)
 	  && *sam != special_array_member::int_0)
 	return memsize;
 
       if (*sam == special_array_member::int_0)
-	  memsize = NULL_TREE;
+	memsize = NULL_TREE;
 
       /* For a reference to a flexible array member of a union
 	 use the size of the union instead of the size of the member.  */
@@ -13129,7 +13142,7 @@ component_ref_size (tree ref, special_array_member *sam /* = NULL */)
 	memsize = TYPE_SIZE_UNIT (argtype);
     }
 
-  /* MEMBER is either a bona fide flexible array member, or a zero-length
+  /* MEMBER is either a bona fide flexible array member, or a zero-elements
      array member, or an array of length one treated as such.  */
 
   /* If the reference is to a declared object and the member a true
diff --git a/gcc/tree.h b/gcc/tree.h
index 91375f9652f7ea349b524fe2e0c6e53bc1131215..abcdb5638d49aea4ccc46efa8e540b1fa78aa27a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5579,8 +5579,8 @@ extern tree component_ref_field_offset (tree);
 enum struct special_array_member
   {
     none,	/* Not a special array member.  */
-    int_0,	/* Interior array member with size zero.  */
-    trail_0,	/* Trailing array member with size zero.  */
+    int_0,	/* Interior array member with zero elements.  */
+    trail_0,	/* Trailing array member with zero elements.  */
     trail_1,	/* Trailing array member with one element.  */
     trail_n,	/* Trailing array member with two or more elements.  */
     int_n	/* Interior array member with one or more elements.  */