[V2] Handle component_ref to a structre/union field including flexible array member [PR101832]

Message ID 20230206134723.3391910-1-qing.zhao@oracle.com
State New
Headers
Series [V2] Handle component_ref to a structre/union field including flexible array member [PR101832] |

Commit Message

Qing Zhao Feb. 6, 2023, 1:47 p.m. UTC
  This is the 2nd version of the patch, compare to the first version, the major
changes are:

  1. Add a new IR bit in tree_type_common: type_include_flexarray, set this bit
     in FE for struct/union types that include a flexible array member (per
     -fstrict-flex-arrays) at the end.
  2. Check this new bit in tree-objec-size.cc to decide whether a component_ref
     to a struct/union has flexible size or not.
  3. Update the testing case per Seddhish's previous change.

Bootstrapped and regression tested on both X86 and aarch64, no issue.

Okay for commit?

thanks.

Qing.
============================

GCC extension accepts the case when a struct with a flexible array member
is embedded into another struct or union (possibly recursively).
__builtin_object_size should treat such struct as flexible size per
-fstrict-flex-arrays.

gcc/c/ChangeLog:

	PR tree-optimization/101832
	* c-decl.cc (finish_struct): Set TYPE_INCLUDE_FLEXARRAY for
	struct/union type.

gcc/ChangeLog:

	PR tree-optimization/101832
	* tree-core.h (struct tree_type_common): New bit
	type_include_flexarray.
	* tree-object-size.cc (addr_object_size): Handle structure/union type
	when it has flexible size.
	* tree.h (TYPE_INCLUDE_FLEXARRAY): New macro
	TYPE_INCLUDE_FLEXARRAY.

gcc/testsuite/ChangeLog:

	PR tree-optimization/101832
	* gcc.dg/builtin-object-size-pr101832-2.c: New test.
	* gcc.dg/builtin-object-size-pr101832-3.c: New test.
	* gcc.dg/builtin-object-size-pr101832-4.c: New test.
	* gcc.dg/builtin-object-size-pr101832-5.c: New test.
	* gcc.dg/builtin-object-size-pr101832.c: New test.
---
 gcc/c/c-decl.cc                               |  11 ++
 .../gcc.dg/builtin-object-size-pr101832-2.c   | 132 ++++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832-3.c   | 132 ++++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832-4.c   | 132 ++++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832-5.c   | 120 ++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832.c     | 115 +++++++++++++++
 gcc/tree-core.h                               |   4 +-
 gcc/tree-object-size.cc                       |  79 +++++++----
 gcc/tree.h                                    |   6 +
 9 files changed, 702 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
  

Comments

Qing Zhao Feb. 8, 2023, 3:51 p.m. UTC | #1
I found a bug with this patch, will fix it and send out the updated patch.

Please ignore this one.

sorry.

Qing

> On Feb 6, 2023, at 8:47 AM, Qing Zhao <qing.zhao@oracle.com> wrote:
> 
> This is the 2nd version of the patch, compare to the first version, the major
> changes are:
> 
>  1. Add a new IR bit in tree_type_common: type_include_flexarray, set this bit
>     in FE for struct/union types that include a flexible array member (per
>     -fstrict-flex-arrays) at the end.
>  2. Check this new bit in tree-objec-size.cc to decide whether a component_ref
>     to a struct/union has flexible size or not.
>  3. Update the testing case per Seddhish's previous change.
> 
> Bootstrapped and regression tested on both X86 and aarch64, no issue.
> 
> Okay for commit?
> 
> thanks.
> 
> Qing.
> ============================
> 
> GCC extension accepts the case when a struct with a flexible array member
> is embedded into another struct or union (possibly recursively).
> __builtin_object_size should treat such struct as flexible size per
> -fstrict-flex-arrays.
> 
> gcc/c/ChangeLog:
> 
> 	PR tree-optimization/101832
> 	* c-decl.cc (finish_struct): Set TYPE_INCLUDE_FLEXARRAY for
> 	struct/union type.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/101832
> 	* tree-core.h (struct tree_type_common): New bit
> 	type_include_flexarray.
> 	* tree-object-size.cc (addr_object_size): Handle structure/union type
> 	when it has flexible size.
> 	* tree.h (TYPE_INCLUDE_FLEXARRAY): New macro
> 	TYPE_INCLUDE_FLEXARRAY.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/101832
> 	* gcc.dg/builtin-object-size-pr101832-2.c: New test.
> 	* gcc.dg/builtin-object-size-pr101832-3.c: New test.
> 	* gcc.dg/builtin-object-size-pr101832-4.c: New test.
> 	* gcc.dg/builtin-object-size-pr101832-5.c: New test.
> 	* gcc.dg/builtin-object-size-pr101832.c: New test.
> ---
> gcc/c/c-decl.cc                               |  11 ++
> .../gcc.dg/builtin-object-size-pr101832-2.c   | 132 ++++++++++++++++++
> .../gcc.dg/builtin-object-size-pr101832-3.c   | 132 ++++++++++++++++++
> .../gcc.dg/builtin-object-size-pr101832-4.c   | 132 ++++++++++++++++++
> .../gcc.dg/builtin-object-size-pr101832-5.c   | 120 ++++++++++++++++
> .../gcc.dg/builtin-object-size-pr101832.c     | 115 +++++++++++++++
> gcc/tree-core.h                               |   4 +-
> gcc/tree-object-size.cc                       |  79 +++++++----
> gcc/tree.h                                    |   6 +
> 9 files changed, 702 insertions(+), 29 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c
> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> 
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 20e7d1855bf..52fef7d84fa 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -9277,6 +9277,17 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>       /* Set DECL_NOT_FLEXARRAY flag for FIELD_DECL x.  */
>       DECL_NOT_FLEXARRAY (x) = !is_flexible_array_member_p (is_last_field, x);
> 
> +      /* Set TYPE_INCLUDE_FLEXARRAY for the context of x,
> +	 i.e., the RECORD_TYPE of UNION_TYPE, t.  */
> +      TYPE_INCLUDE_FLEXARRAY (t) = !DECL_NOT_FLEXARRAY (x);
> +
> +      /* Recursively set TYPE_INCLUDE_FLEXARRAY for the context of x, t
> +	 when x is the last field.  */
> +      if ((TREE_TYPE (x) != error_mark_node)
> +	   && TYPE_INCLUDE_FLEXARRAY (TREE_TYPE (x))
> +	   && is_last_field)
> +	TYPE_INCLUDE_FLEXARRAY (t) = true;
> +
>       if (DECL_NAME (x)
> 	  || RECORD_OR_UNION_TYPE_P (TREE_TYPE (x)))
> 	saw_named_field = true;
> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> new file mode 100644
> index 00000000000..af9769d73e0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> @@ -0,0 +1,132 @@
> +/* PR 101832: 
> +   GCC extension accepts the case when a struct with a flexible array member
> +   is embedded into another struct (possibly recursively).
> +   __builtin_object_size will treat such struct as flexible size per
> +   -fstrict-flex-arrays.  */ 
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fstrict-flex-arrays=1" } */
> +
> +#include "builtin-object-size-common.h"
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    FAIL (); \
> +  } \
> +} while (0);
> +
> +
> +struct A {
> +  int n;
> +  char data[];
> +};
> +
> +struct B {
> +  int m;
> +  struct A a;
> +};
> +
> +struct C {
> +  int q;
> +  struct B b;
> +};
> +
> +struct A0 {
> +  int n;
> +  char data[0];
> +};
> +
> +struct B0 {
> +  int m;
> +  struct A0 a;
> +};
> +
> +struct C0 {
> +  int q;
> +  struct B0 b;
> +};
> +
> +struct A1 {
> +  int n;
> +  char data[1];
> +};
> +
> +struct B1 {
> +  int m;
> +  struct A1 a;
> +};
> +
> +struct C1 {
> +  int q;
> +  struct B1 b;
> +};
> +
> +struct An {
> +  int n;
> +  char data[8];
> +};
> +
> +struct Bn {
> +  int m;
> +  struct An a;
> +};
> +
> +struct Cn {
> +  int q;
> +  struct Bn b;
> +};
> +
> +volatile void *magic1, *magic2;
> +
> +int main (int argc, char *argv[])
> +{
> +  struct B *outer;
> +  struct C *outest;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer = (void *)magic1;
> +  outest = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer->a, 1), -1);
> +  expect (__builtin_object_size (&outest->b, 1), -1);
> +  expect (__builtin_object_size (&outest->b.a, 1), -1);
> +
> +  struct B0 *outer0;
> +  struct C0 *outest0;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer0 = (void *)magic1;
> +  outest0 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer0->a, 1), -1);
> +  expect (__builtin_object_size (&outest0->b, 1), -1);
> +  expect (__builtin_object_size (&outest0->b.a, 1), -1);
> +
> +  struct B1 *outer1;
> +  struct C1 *outest1;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer1 = (void *)magic1;
> +  outest1 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer1->a, 1), -1);
> +  expect (__builtin_object_size (&outest1->b, 1), -1);
> +  expect (__builtin_object_size (&outest1->b.a, 1), -1);
> +
> +  struct Bn *outern;
> +  struct Cn *outestn;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outern = (void *)magic1;
> +  outestn = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outern->a, 1), sizeof (outern->a));
> +  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
> +  expect (__builtin_object_size (&outestn->b.a, 1), sizeof (outestn->b.a));
> +
> +  DONE ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> new file mode 100644
> index 00000000000..0d98c3246f3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> @@ -0,0 +1,132 @@
> +/* PR 101832: 
> +   GCC extension accepts the case when a struct with a flexible array member
> +   is embedded into another struct (possibly recursively).
> +   __builtin_object_size will treat such struct as flexible size per
> +   -fstrict-flex-arrays.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fstrict-flex-arrays=2" } */
> +
> +#include "builtin-object-size-common.h"
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    FAIL (); \
> +  } \
> +} while (0);
> +
> +
> +struct A {
> +  int n;
> +  char data[];/* Content following header */
> +};
> +
> +struct B {
> +  int m;
> +  struct A a;
> +};
> +
> +struct C {
> +  int q;
> +  struct B b;
> +};
> +
> +struct A0 {
> +  int n;
> +  char data[0];/* Content following header */
> +};
> +
> +struct B0 {
> +  int m;
> +  struct A0 a;
> +};
> +
> +struct C0 {
> +  int q;
> +  struct B0 b;
> +};
> +
> +struct A1 {
> +  int n;
> +  char data[1];/* Content following header */
> +};
> +
> +struct B1 {
> +  int m;
> +  struct A1 a;
> +};
> +
> +struct C1 {
> +  int q;
> +  struct B1 b;
> +};
> +
> +struct An {
> +  int n;
> +  char data[8];/* Content following header */
> +};
> +
> +struct Bn {
> +  int m;
> +  struct An a;
> +};
> +
> +struct Cn {
> +  int q;
> +  struct Bn b;
> +};
> +
> +volatile void *magic1, *magic2;
> +
> +int main (int argc, char *argv[])
> +{
> +  struct B *outer;
> +  struct C *outest;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer = (void *)magic1;
> +  outest = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer->a, 1), -1);
> +  expect (__builtin_object_size (&outest->b, 1), -1);
> +  expect (__builtin_object_size (&outest->b.a, 1), -1);
> +
> +  struct B0 *outer0;
> +  struct C0 *outest0;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer0 = (void *)magic1;
> +  outest0 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer0->a, 1), -1);
> +  expect (__builtin_object_size (&outest0->b, 1), -1);
> +  expect (__builtin_object_size (&outest0->b.a, 1), -1);
> +
> +  struct B1 *outer1;
> +  struct C1 *outest1;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer1 = (void *)magic1;
> +  outest1 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer1->a, 1), sizeof (outer1->a));
> +  expect (__builtin_object_size (&outest1->b, 1), sizeof (outest1->b));
> +  expect (__builtin_object_size (&outest1->b.a, 1), sizeof (outest1->b.a));
> +
> +  struct Bn *outern;
> +  struct Cn *outestn;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outern = (void *)magic1;
> +  outestn = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outern->a, 1), sizeof (outern->a));
> +  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
> +  expect (__builtin_object_size (&outestn->b.a, 1), sizeof (outestn->b.a));
> +
> +  DONE ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> new file mode 100644
> index 00000000000..9de4c8b74cd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> @@ -0,0 +1,132 @@
> +/* PR 101832: 
> +   GCC extension accepts the case when a struct with a flexible array member
> +   is embedded into another struct (possibly recursively).
> +   __builtin_object_size will treat such struct as flexible size per
> +   -fstrict-flex-arrays.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fstrict-flex-arrays=3" } */
> +
> +#include "builtin-object-size-common.h"
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    FAIL (); \
> +  } \
> +} while (0);
> +
> +
> +struct A {
> +  int n;
> +  char data[];
> +};
> +
> +struct B {
> +  int m;
> +  struct A a;
> +};
> +
> +struct C {
> +  int q;
> +  struct B b;
> +};
> +
> +struct A0 {
> +  int n;
> +  char data[0];
> +};
> +
> +struct B0 {
> +  int m;
> +  struct A0 a;
> +};
> +
> +struct C0 {
> +  int q;
> +  struct B0 b;
> +};
> +
> +struct A1 {
> +  int n;
> +  char data[1];
> +};
> +
> +struct B1 {
> +  int m;
> +  struct A1 a;
> +};
> +
> +struct C1 {
> +  int q;
> +  struct B1 b;
> +};
> +
> +struct An {
> +  int n;
> +  char data[8];
> +};
> +
> +struct Bn {
> +  int m;
> +  struct An a;
> +};
> +
> +struct Cn {
> +  int q;
> +  struct Bn b;
> +};
> +
> +volatile void *magic1, *magic2;
> +
> +int main (int argc, char *argv[])
> +{
> +  struct B *outer;
> +  struct C *outest;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer = (void *)magic1;
> +  outest = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer->a, 1), -1);
> +  expect (__builtin_object_size (&outest->b, 1), -1);
> +  expect (__builtin_object_size (&outest->b.a, 1), -1);
> +
> +  struct B0 *outer0;
> +  struct C0 *outest0;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer0 = (void *)magic1;
> +  outest0 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer0->a, 1), sizeof (outer0->a));
> +  expect (__builtin_object_size (&outest0->b, 1), sizeof (outest0->b));
> +  expect (__builtin_object_size (&outest0->b.a, 1), sizeof (outest0->b.a));
> +
> +  struct B1 *outer1;
> +  struct C1 *outest1;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outer1 = (void *)magic1;
> +  outest1 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer1->a, 1), sizeof (outer1->a));
> +  expect (__builtin_object_size (&outest1->b, 1), sizeof (outest1->b));
> +  expect (__builtin_object_size (&outest1->b.a, 1), sizeof (outest1->b.a));
> +
> +  struct Bn *outern;
> +  struct Cn *outestn;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outern = (void *)magic1;
> +  outestn = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outern->a, 1), sizeof (outern->a));
> +  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
> +  expect (__builtin_object_size (&outestn->b.a, 1), sizeof (outestn->b.a));
> +
> +  DONE ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c
> new file mode 100644
> index 00000000000..0bebe9f48ba
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c
> @@ -0,0 +1,120 @@
> +/* PR 101832: 
> +   GCC extension accepts the case when a struct with a flexible array member
> +   is embedded into another struct or union (possibly recursively).
> +   __builtin_object_size will treat such struct as flexible size per
> +   -fstrict-flex-arrays.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fstrict-flex-arrays=3" } */
> +
> +#include "builtin-object-size-common.h"
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    __builtin_printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    __builtin_printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    FAIL (); \
> +  } \
> +} while (0);
> +
> +
> +struct A {
> +  int n;
> +  char dataA[];
> +};
> +
> +struct B {
> +  int m;
> +  char dataB[]; 
> +};
> +
> +union C {
> +  struct A a;
> +  struct B b;
> +};
> +
> +struct A0 {
> +  int n;
> +  char dataA0[0];
> +};
> +
> +struct B0 {
> +  int m;
> +  char dataB0[0];
> +};
> +
> +union C0 {
> +  struct A0 a;
> +  struct B0 b;
> +};
> +
> +struct A1 {
> +  int n;
> +  char dataA1[1];
> +};
> +
> +struct B1 {
> +  int m;
> +  char dataB1[1];
> +};
> +
> +union C1 {
> +  struct A1 a;
> +  struct B1 b;
> +};
> +
> +struct An {
> +  int n;
> +  char dataAn[8];
> +};
> +
> +struct Bn {
> +  int m;
> +  char dataBn[8];
> +};
> +
> +union Cn {
> +  struct An a;
> +  struct Bn b;
> +};
> +
> +volatile void *magic;
> +
> +int main(int argc, char *argv[])
> +{
> +  union C *outest;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outest = (void *)magic;
> +
> +  expect (__builtin_object_size (&outest->a, 1), -1);
> +  expect (__builtin_object_size (&outest->b, 1), -1);
> +
> +  union C0 *outest0;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outest0 = (void *)magic;
> +
> +  expect (__builtin_object_size (&outest0->a, 1), sizeof (outest0->a));
> +  expect (__builtin_object_size (&outest0->b, 1), sizeof (outest0->b));
> +
> +  union C1 *outest1;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outest1 = (void *)magic;
> +
> +  expect (__builtin_object_size (&outest1->a, 1), sizeof (outest1->a));
> +  expect (__builtin_object_size (&outest1->b, 1), sizeof (outest1->b));
> +
> +  union Cn *outestn;
> +
> +  /* Make sure optimization can't find some other object size. */
> +  outestn = (void *)magic;
> +
> +  expect (__builtin_object_size (&outestn->a, 1), sizeof (outestn->a));
> +  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
> +
> +  DONE ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> new file mode 100644
> index 00000000000..e4482800191
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> @@ -0,0 +1,115 @@
> +/* PR 101832: 
> +   GCC extension accepts the case when a struct with a flexible array member
> +   is embedded into another struct (possibly recursively).
> +   __builtin_object_size will treat such struct as flexible size per
> +   -fstrict-flex-arrays.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +#include "builtin-object-size-common.h"
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    FAIL (); \
> +  } \
> +} while (0);
> +
> +struct A {
> +  int n;
> +  char data[];
> +};
> +
> +struct B {
> +  int m;
> +  struct A a;
> +};
> +
> +struct C {
> +  int q;
> +  struct B b;
> +};
> +
> +struct A0 {
> +  int n;
> +  char data[0];
> +};
> +
> +struct B0 {
> +  int m;
> +  struct A0 a;
> +};
> +
> +struct C0 {
> +  int q;
> +  struct B0 b;
> +};
> +
> +struct A1 {
> +  int n;
> +  char data[1];
> +};
> +
> +struct B1 {
> +  int m;
> +  struct A1 a;
> +};
> +
> +struct C1 {
> +  int q;
> +  struct B1 b;
> +};
> +
> +struct An {
> +  int n;
> +  char data[8];
> +};
> +
> +struct Bn {
> +  int m;
> +  struct An a;
> +};
> +
> +struct Cn {
> +  int q;
> +  struct Bn b;
> +};
> +
> +volatile void *magic1, *magic2;
> +
> +int main (int argc, char *argv[])
> +{
> +  struct B *outer = (void *)magic1;
> +  struct C *outest = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer->a, 1), -1);
> +  expect (__builtin_object_size (&outest->b, 1), -1);
> +  expect (__builtin_object_size (&outest->b.a, 1), -1);
> +
> +  struct B0 *outer0 = (void *)magic1;
> +  struct C0 *outest0 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer0->a, 1), -1);
> +  expect (__builtin_object_size (&outest0->b, 1), -1);
> +  expect (__builtin_object_size (&outest0->b.a, 1), -1);
> +
> +  struct B1 *outer1 = (void *)magic1;
> +  struct C1 *outest1 = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outer1->a, 1), -1);
> +  expect (__builtin_object_size (&outest1->b, 1), -1);
> +  expect (__builtin_object_size (&outest1->b.a, 1), -1);
> +
> +  struct Bn *outern = (void *)magic1;
> +  struct Cn *outestn = (void *)magic2;
> +
> +  expect (__builtin_object_size (&outern->a, 1), -1);
> +  expect (__builtin_object_size (&outestn->b, 1), -1);
> +  expect (__builtin_object_size (&outestn->b.a, 1), -1);
> +
> +  DONE ();
> +  return 0;
> +}
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index acd8deea34e..705d5702b9c 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1718,7 +1718,9 @@ struct GTY(()) tree_type_common {
>   unsigned empty_flag : 1;
>   unsigned indivisible_p : 1;
>   unsigned no_named_args_stdarg_p : 1;
> -  unsigned spare : 15;
> +  /* TYPE_INCLUDE_FLEXARRAY flag for RECORD_TYPE and UNION_TYPE.  */
> +  unsigned int type_include_flexarray : 1;
> +  unsigned spare : 14;
> 
>   alias_set_type alias_set;
>   tree pointer_to;
> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
> index 9a936a91983..22b3c72ea6e 100644
> --- a/gcc/tree-object-size.cc
> +++ b/gcc/tree-object-size.cc
> @@ -633,45 +633,68 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
> 		    v = NULL_TREE;
> 		    break;
> 		  case COMPONENT_REF:
> -		    if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE)
> +		    /* When the ref is not to an array, a record or a union, it
> +		       will not have flexible size, compute the object size
> +		       directly.  */
> +		    if ((TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE)
> +			&& (TREE_CODE (TREE_TYPE (v)) != RECORD_TYPE)
> +			&& (TREE_CODE (TREE_TYPE (v)) != UNION_TYPE))
> 		      {
> 			v = NULL_TREE;
> 			break;
> 		      }
> -		    is_flexible_array_mem_ref = array_ref_flexible_size_p (v);
> -		    while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
> -		      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> -			  != UNION_TYPE
> -			  && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> -			  != QUAL_UNION_TYPE)
> -			break;
> -		      else
> -			v = TREE_OPERAND (v, 0);
> -		    if (TREE_CODE (v) == COMPONENT_REF
> -			&& TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> -			   == RECORD_TYPE)
> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
> +		    /* if the record or union does not include a flexible array
> +		       recursively, compute the object size directly.  */
> 		      {
> -			/* compute object size only if v is not a
> -			   flexible array member.  */
> -			if (!is_flexible_array_mem_ref)
> +			if (!TYPE_INCLUDE_FLEXARRAY (TREE_TYPE (v)))
> 			  {
> 			    v = NULL_TREE;
> 			    break;
> 			  }
> -			v = TREE_OPERAND (v, 0);
> +			else
> +			  v = TREE_OPERAND (v, 0);
> 		      }
> -		    while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
> -		      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> -			  != UNION_TYPE
> -			  && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> -			  != QUAL_UNION_TYPE)
> -			break;
> -		      else
> -			v = TREE_OPERAND (v, 0);
> -		    if (v != pt_var)
> -		      v = NULL_TREE;
> 		    else
> -		      v = pt_var;
> +		      {
> +			/* Now the ref is to an array type.  */
> +			is_flexible_array_mem_ref
> +			  = array_ref_flexible_size_p (v);
> +			while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
> +			if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> +			      != UNION_TYPE
> +			    && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> +				 != QUAL_UNION_TYPE)
> +			  break;
> +			else
> +			  v = TREE_OPERAND (v, 0);
> +			if (TREE_CODE (v) == COMPONENT_REF
> +			    && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> +				 == RECORD_TYPE)
> +			  {
> +			    /* compute object size only if v is not a
> +			       flexible array member.  */
> +			    if (!is_flexible_array_mem_ref)
> +			      {
> +				v = NULL_TREE;
> +				break;
> +			      }
> +			    v = TREE_OPERAND (v, 0);
> +			  }
> +			while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
> +			  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> +				!= UNION_TYPE
> +			      && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
> +				   != QUAL_UNION_TYPE)
> +			    break;
> +			  else
> +			    v = TREE_OPERAND (v, 0);
> +			if (v != pt_var)
> +			  v = NULL_TREE;
> +			else
> +			  v = pt_var;
> +		      }
> 		    break;
> 		  default:
> 		    v = pt_var;
> diff --git a/gcc/tree.h b/gcc/tree.h
> index c656cd5b7bf..a27b9bb43bc 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -778,6 +778,12 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
> #define TYPE_NO_NAMED_ARGS_STDARG_P(NODE) \
>   (TYPE_CHECK (NODE)->type_common.no_named_args_stdarg_p)
> 
> +/* True if this RECORD_TYPE or UNION_TYPE includes a flexible array member
> +   at the last field recursively.  */
> +#define TYPE_INCLUDE_FLEXARRAY(NODE) \
> +  (TYPE_CHECK (NODE)->type_common.type_include_flexarray)
> +
> +
> /* In an IDENTIFIER_NODE, this means that assemble_name was called with
>    this string as an argument.  */
> #define TREE_SYMBOL_REFERENCED(NODE) \
> -- 
> 2.31.1
>
  

Patch

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 20e7d1855bf..52fef7d84fa 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9277,6 +9277,17 @@  finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
       /* Set DECL_NOT_FLEXARRAY flag for FIELD_DECL x.  */
       DECL_NOT_FLEXARRAY (x) = !is_flexible_array_member_p (is_last_field, x);
 
+      /* Set TYPE_INCLUDE_FLEXARRAY for the context of x,
+	 i.e., the RECORD_TYPE of UNION_TYPE, t.  */
+      TYPE_INCLUDE_FLEXARRAY (t) = !DECL_NOT_FLEXARRAY (x);
+
+      /* Recursively set TYPE_INCLUDE_FLEXARRAY for the context of x, t
+	 when x is the last field.  */
+      if ((TREE_TYPE (x) != error_mark_node)
+	   && TYPE_INCLUDE_FLEXARRAY (TREE_TYPE (x))
+	   && is_last_field)
+	TYPE_INCLUDE_FLEXARRAY (t) = true;
+
       if (DECL_NAME (x)
 	  || RECORD_OR_UNION_TYPE_P (TREE_TYPE (x)))
 	saw_named_field = true;
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
new file mode 100644
index 00000000000..af9769d73e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
@@ -0,0 +1,132 @@ 
+/* PR 101832: 
+   GCC extension accepts the case when a struct with a flexible array member
+   is embedded into another struct (possibly recursively).
+   __builtin_object_size will treat such struct as flexible size per
+   -fstrict-flex-arrays.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2 -fstrict-flex-arrays=1" } */
+
+#include "builtin-object-size-common.h"
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
+  else {\
+    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    FAIL (); \
+  } \
+} while (0);
+
+
+struct A {
+  int n;
+  char data[];
+};
+
+struct B {
+  int m;
+  struct A a;
+};
+
+struct C {
+  int q;
+  struct B b;
+};
+
+struct A0 {
+  int n;
+  char data[0];
+};
+
+struct B0 {
+  int m;
+  struct A0 a;
+};
+
+struct C0 {
+  int q;
+  struct B0 b;
+};
+
+struct A1 {
+  int n;
+  char data[1];
+};
+
+struct B1 {
+  int m;
+  struct A1 a;
+};
+
+struct C1 {
+  int q;
+  struct B1 b;
+};
+
+struct An {
+  int n;
+  char data[8];
+};
+
+struct Bn {
+  int m;
+  struct An a;
+};
+
+struct Cn {
+  int q;
+  struct Bn b;
+};
+
+volatile void *magic1, *magic2;
+
+int main (int argc, char *argv[])
+{
+  struct B *outer;
+  struct C *outest;
+
+  /* Make sure optimization can't find some other object size. */
+  outer = (void *)magic1;
+  outest = (void *)magic2;
+
+  expect (__builtin_object_size (&outer->a, 1), -1);
+  expect (__builtin_object_size (&outest->b, 1), -1);
+  expect (__builtin_object_size (&outest->b.a, 1), -1);
+
+  struct B0 *outer0;
+  struct C0 *outest0;
+
+  /* Make sure optimization can't find some other object size. */
+  outer0 = (void *)magic1;
+  outest0 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer0->a, 1), -1);
+  expect (__builtin_object_size (&outest0->b, 1), -1);
+  expect (__builtin_object_size (&outest0->b.a, 1), -1);
+
+  struct B1 *outer1;
+  struct C1 *outest1;
+
+  /* Make sure optimization can't find some other object size. */
+  outer1 = (void *)magic1;
+  outest1 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer1->a, 1), -1);
+  expect (__builtin_object_size (&outest1->b, 1), -1);
+  expect (__builtin_object_size (&outest1->b.a, 1), -1);
+
+  struct Bn *outern;
+  struct Cn *outestn;
+
+  /* Make sure optimization can't find some other object size. */
+  outern = (void *)magic1;
+  outestn = (void *)magic2;
+
+  expect (__builtin_object_size (&outern->a, 1), sizeof (outern->a));
+  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
+  expect (__builtin_object_size (&outestn->b.a, 1), sizeof (outestn->b.a));
+
+  DONE ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
new file mode 100644
index 00000000000..0d98c3246f3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
@@ -0,0 +1,132 @@ 
+/* PR 101832: 
+   GCC extension accepts the case when a struct with a flexible array member
+   is embedded into another struct (possibly recursively).
+   __builtin_object_size will treat such struct as flexible size per
+   -fstrict-flex-arrays.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fstrict-flex-arrays=2" } */
+
+#include "builtin-object-size-common.h"
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
+  else {\
+    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    FAIL (); \
+  } \
+} while (0);
+
+
+struct A {
+  int n;
+  char data[];/* Content following header */
+};
+
+struct B {
+  int m;
+  struct A a;
+};
+
+struct C {
+  int q;
+  struct B b;
+};
+
+struct A0 {
+  int n;
+  char data[0];/* Content following header */
+};
+
+struct B0 {
+  int m;
+  struct A0 a;
+};
+
+struct C0 {
+  int q;
+  struct B0 b;
+};
+
+struct A1 {
+  int n;
+  char data[1];/* Content following header */
+};
+
+struct B1 {
+  int m;
+  struct A1 a;
+};
+
+struct C1 {
+  int q;
+  struct B1 b;
+};
+
+struct An {
+  int n;
+  char data[8];/* Content following header */
+};
+
+struct Bn {
+  int m;
+  struct An a;
+};
+
+struct Cn {
+  int q;
+  struct Bn b;
+};
+
+volatile void *magic1, *magic2;
+
+int main (int argc, char *argv[])
+{
+  struct B *outer;
+  struct C *outest;
+
+  /* Make sure optimization can't find some other object size. */
+  outer = (void *)magic1;
+  outest = (void *)magic2;
+
+  expect (__builtin_object_size (&outer->a, 1), -1);
+  expect (__builtin_object_size (&outest->b, 1), -1);
+  expect (__builtin_object_size (&outest->b.a, 1), -1);
+
+  struct B0 *outer0;
+  struct C0 *outest0;
+
+  /* Make sure optimization can't find some other object size. */
+  outer0 = (void *)magic1;
+  outest0 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer0->a, 1), -1);
+  expect (__builtin_object_size (&outest0->b, 1), -1);
+  expect (__builtin_object_size (&outest0->b.a, 1), -1);
+
+  struct B1 *outer1;
+  struct C1 *outest1;
+
+  /* Make sure optimization can't find some other object size. */
+  outer1 = (void *)magic1;
+  outest1 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer1->a, 1), sizeof (outer1->a));
+  expect (__builtin_object_size (&outest1->b, 1), sizeof (outest1->b));
+  expect (__builtin_object_size (&outest1->b.a, 1), sizeof (outest1->b.a));
+
+  struct Bn *outern;
+  struct Cn *outestn;
+
+  /* Make sure optimization can't find some other object size. */
+  outern = (void *)magic1;
+  outestn = (void *)magic2;
+
+  expect (__builtin_object_size (&outern->a, 1), sizeof (outern->a));
+  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
+  expect (__builtin_object_size (&outestn->b.a, 1), sizeof (outestn->b.a));
+
+  DONE ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
new file mode 100644
index 00000000000..9de4c8b74cd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
@@ -0,0 +1,132 @@ 
+/* PR 101832: 
+   GCC extension accepts the case when a struct with a flexible array member
+   is embedded into another struct (possibly recursively).
+   __builtin_object_size will treat such struct as flexible size per
+   -fstrict-flex-arrays.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fstrict-flex-arrays=3" } */
+
+#include "builtin-object-size-common.h"
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
+  else {\
+    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    FAIL (); \
+  } \
+} while (0);
+
+
+struct A {
+  int n;
+  char data[];
+};
+
+struct B {
+  int m;
+  struct A a;
+};
+
+struct C {
+  int q;
+  struct B b;
+};
+
+struct A0 {
+  int n;
+  char data[0];
+};
+
+struct B0 {
+  int m;
+  struct A0 a;
+};
+
+struct C0 {
+  int q;
+  struct B0 b;
+};
+
+struct A1 {
+  int n;
+  char data[1];
+};
+
+struct B1 {
+  int m;
+  struct A1 a;
+};
+
+struct C1 {
+  int q;
+  struct B1 b;
+};
+
+struct An {
+  int n;
+  char data[8];
+};
+
+struct Bn {
+  int m;
+  struct An a;
+};
+
+struct Cn {
+  int q;
+  struct Bn b;
+};
+
+volatile void *magic1, *magic2;
+
+int main (int argc, char *argv[])
+{
+  struct B *outer;
+  struct C *outest;
+
+  /* Make sure optimization can't find some other object size. */
+  outer = (void *)magic1;
+  outest = (void *)magic2;
+
+  expect (__builtin_object_size (&outer->a, 1), -1);
+  expect (__builtin_object_size (&outest->b, 1), -1);
+  expect (__builtin_object_size (&outest->b.a, 1), -1);
+
+  struct B0 *outer0;
+  struct C0 *outest0;
+
+  /* Make sure optimization can't find some other object size. */
+  outer0 = (void *)magic1;
+  outest0 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer0->a, 1), sizeof (outer0->a));
+  expect (__builtin_object_size (&outest0->b, 1), sizeof (outest0->b));
+  expect (__builtin_object_size (&outest0->b.a, 1), sizeof (outest0->b.a));
+
+  struct B1 *outer1;
+  struct C1 *outest1;
+
+  /* Make sure optimization can't find some other object size. */
+  outer1 = (void *)magic1;
+  outest1 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer1->a, 1), sizeof (outer1->a));
+  expect (__builtin_object_size (&outest1->b, 1), sizeof (outest1->b));
+  expect (__builtin_object_size (&outest1->b.a, 1), sizeof (outest1->b.a));
+
+  struct Bn *outern;
+  struct Cn *outestn;
+
+  /* Make sure optimization can't find some other object size. */
+  outern = (void *)magic1;
+  outestn = (void *)magic2;
+
+  expect (__builtin_object_size (&outern->a, 1), sizeof (outern->a));
+  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
+  expect (__builtin_object_size (&outestn->b.a, 1), sizeof (outestn->b.a));
+
+  DONE ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c
new file mode 100644
index 00000000000..0bebe9f48ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-5.c
@@ -0,0 +1,120 @@ 
+/* PR 101832: 
+   GCC extension accepts the case when a struct with a flexible array member
+   is embedded into another struct or union (possibly recursively).
+   __builtin_object_size will treat such struct as flexible size per
+   -fstrict-flex-arrays.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fstrict-flex-arrays=3" } */
+
+#include "builtin-object-size-common.h"
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    __builtin_printf("ok:  %s == %zd\n", #p, p); \
+  else {\
+    __builtin_printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    FAIL (); \
+  } \
+} while (0);
+
+
+struct A {
+  int n;
+  char dataA[];
+};
+
+struct B {
+  int m;
+  char dataB[]; 
+};
+
+union C {
+  struct A a;
+  struct B b;
+};
+
+struct A0 {
+  int n;
+  char dataA0[0];
+};
+
+struct B0 {
+  int m;
+  char dataB0[0];
+};
+
+union C0 {
+  struct A0 a;
+  struct B0 b;
+};
+
+struct A1 {
+  int n;
+  char dataA1[1];
+};
+
+struct B1 {
+  int m;
+  char dataB1[1];
+};
+
+union C1 {
+  struct A1 a;
+  struct B1 b;
+};
+
+struct An {
+  int n;
+  char dataAn[8];
+};
+
+struct Bn {
+  int m;
+  char dataBn[8];
+};
+
+union Cn {
+  struct An a;
+  struct Bn b;
+};
+
+volatile void *magic;
+
+int main(int argc, char *argv[])
+{
+  union C *outest;
+
+  /* Make sure optimization can't find some other object size. */
+  outest = (void *)magic;
+
+  expect (__builtin_object_size (&outest->a, 1), -1);
+  expect (__builtin_object_size (&outest->b, 1), -1);
+
+  union C0 *outest0;
+
+  /* Make sure optimization can't find some other object size. */
+  outest0 = (void *)magic;
+
+  expect (__builtin_object_size (&outest0->a, 1), sizeof (outest0->a));
+  expect (__builtin_object_size (&outest0->b, 1), sizeof (outest0->b));
+
+  union C1 *outest1;
+
+  /* Make sure optimization can't find some other object size. */
+  outest1 = (void *)magic;
+
+  expect (__builtin_object_size (&outest1->a, 1), sizeof (outest1->a));
+  expect (__builtin_object_size (&outest1->b, 1), sizeof (outest1->b));
+
+  union Cn *outestn;
+
+  /* Make sure optimization can't find some other object size. */
+  outestn = (void *)magic;
+
+  expect (__builtin_object_size (&outestn->a, 1), sizeof (outestn->a));
+  expect (__builtin_object_size (&outestn->b, 1), sizeof (outestn->b));
+
+  DONE ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
new file mode 100644
index 00000000000..e4482800191
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
@@ -0,0 +1,115 @@ 
+/* PR 101832: 
+   GCC extension accepts the case when a struct with a flexible array member
+   is embedded into another struct (possibly recursively).
+   __builtin_object_size will treat such struct as flexible size per
+   -fstrict-flex-arrays.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    __builtin_printf ("ok:  %s == %zd\n", #p, p); \
+  else {\
+    __builtin_printf ("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    FAIL (); \
+  } \
+} while (0);
+
+struct A {
+  int n;
+  char data[];
+};
+
+struct B {
+  int m;
+  struct A a;
+};
+
+struct C {
+  int q;
+  struct B b;
+};
+
+struct A0 {
+  int n;
+  char data[0];
+};
+
+struct B0 {
+  int m;
+  struct A0 a;
+};
+
+struct C0 {
+  int q;
+  struct B0 b;
+};
+
+struct A1 {
+  int n;
+  char data[1];
+};
+
+struct B1 {
+  int m;
+  struct A1 a;
+};
+
+struct C1 {
+  int q;
+  struct B1 b;
+};
+
+struct An {
+  int n;
+  char data[8];
+};
+
+struct Bn {
+  int m;
+  struct An a;
+};
+
+struct Cn {
+  int q;
+  struct Bn b;
+};
+
+volatile void *magic1, *magic2;
+
+int main (int argc, char *argv[])
+{
+  struct B *outer = (void *)magic1;
+  struct C *outest = (void *)magic2;
+
+  expect (__builtin_object_size (&outer->a, 1), -1);
+  expect (__builtin_object_size (&outest->b, 1), -1);
+  expect (__builtin_object_size (&outest->b.a, 1), -1);
+
+  struct B0 *outer0 = (void *)magic1;
+  struct C0 *outest0 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer0->a, 1), -1);
+  expect (__builtin_object_size (&outest0->b, 1), -1);
+  expect (__builtin_object_size (&outest0->b.a, 1), -1);
+
+  struct B1 *outer1 = (void *)magic1;
+  struct C1 *outest1 = (void *)magic2;
+
+  expect (__builtin_object_size (&outer1->a, 1), -1);
+  expect (__builtin_object_size (&outest1->b, 1), -1);
+  expect (__builtin_object_size (&outest1->b.a, 1), -1);
+
+  struct Bn *outern = (void *)magic1;
+  struct Cn *outestn = (void *)magic2;
+
+  expect (__builtin_object_size (&outern->a, 1), -1);
+  expect (__builtin_object_size (&outestn->b, 1), -1);
+  expect (__builtin_object_size (&outestn->b.a, 1), -1);
+
+  DONE ();
+  return 0;
+}
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index acd8deea34e..705d5702b9c 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1718,7 +1718,9 @@  struct GTY(()) tree_type_common {
   unsigned empty_flag : 1;
   unsigned indivisible_p : 1;
   unsigned no_named_args_stdarg_p : 1;
-  unsigned spare : 15;
+  /* TYPE_INCLUDE_FLEXARRAY flag for RECORD_TYPE and UNION_TYPE.  */
+  unsigned int type_include_flexarray : 1;
+  unsigned spare : 14;
 
   alias_set_type alias_set;
   tree pointer_to;
diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
index 9a936a91983..22b3c72ea6e 100644
--- a/gcc/tree-object-size.cc
+++ b/gcc/tree-object-size.cc
@@ -633,45 +633,68 @@  addr_object_size (struct object_size_info *osi, const_tree ptr,
 		    v = NULL_TREE;
 		    break;
 		  case COMPONENT_REF:
-		    if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE)
+		    /* When the ref is not to an array, a record or a union, it
+		       will not have flexible size, compute the object size
+		       directly.  */
+		    if ((TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE)
+			&& (TREE_CODE (TREE_TYPE (v)) != RECORD_TYPE)
+			&& (TREE_CODE (TREE_TYPE (v)) != UNION_TYPE))
 		      {
 			v = NULL_TREE;
 			break;
 		      }
-		    is_flexible_array_mem_ref = array_ref_flexible_size_p (v);
-		    while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
-		      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
-			  != UNION_TYPE
-			  && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
-			  != QUAL_UNION_TYPE)
-			break;
-		      else
-			v = TREE_OPERAND (v, 0);
-		    if (TREE_CODE (v) == COMPONENT_REF
-			&& TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
-			   == RECORD_TYPE)
+		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
+			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
+		    /* if the record or union does not include a flexible array
+		       recursively, compute the object size directly.  */
 		      {
-			/* compute object size only if v is not a
-			   flexible array member.  */
-			if (!is_flexible_array_mem_ref)
+			if (!TYPE_INCLUDE_FLEXARRAY (TREE_TYPE (v)))
 			  {
 			    v = NULL_TREE;
 			    break;
 			  }
-			v = TREE_OPERAND (v, 0);
+			else
+			  v = TREE_OPERAND (v, 0);
 		      }
-		    while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
-		      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
-			  != UNION_TYPE
-			  && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
-			  != QUAL_UNION_TYPE)
-			break;
-		      else
-			v = TREE_OPERAND (v, 0);
-		    if (v != pt_var)
-		      v = NULL_TREE;
 		    else
-		      v = pt_var;
+		      {
+			/* Now the ref is to an array type.  */
+			is_flexible_array_mem_ref
+			  = array_ref_flexible_size_p (v);
+			while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
+			if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+			      != UNION_TYPE
+			    && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+				 != QUAL_UNION_TYPE)
+			  break;
+			else
+			  v = TREE_OPERAND (v, 0);
+			if (TREE_CODE (v) == COMPONENT_REF
+			    && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+				 == RECORD_TYPE)
+			  {
+			    /* compute object size only if v is not a
+			       flexible array member.  */
+			    if (!is_flexible_array_mem_ref)
+			      {
+				v = NULL_TREE;
+				break;
+			      }
+			    v = TREE_OPERAND (v, 0);
+			  }
+			while (v != pt_var && TREE_CODE (v) == COMPONENT_REF)
+			  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+				!= UNION_TYPE
+			      && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+				   != QUAL_UNION_TYPE)
+			    break;
+			  else
+			    v = TREE_OPERAND (v, 0);
+			if (v != pt_var)
+			  v = NULL_TREE;
+			else
+			  v = pt_var;
+		      }
 		    break;
 		  default:
 		    v = pt_var;
diff --git a/gcc/tree.h b/gcc/tree.h
index c656cd5b7bf..a27b9bb43bc 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -778,6 +778,12 @@  extern void omp_clause_range_check_failed (const_tree, const char *, int,
 #define TYPE_NO_NAMED_ARGS_STDARG_P(NODE) \
   (TYPE_CHECK (NODE)->type_common.no_named_args_stdarg_p)
 
+/* True if this RECORD_TYPE or UNION_TYPE includes a flexible array member
+   at the last field recursively.  */
+#define TYPE_INCLUDE_FLEXARRAY(NODE) \
+  (TYPE_CHECK (NODE)->type_common.type_include_flexarray)
+
+
 /* In an IDENTIFIER_NODE, this means that assemble_name was called with
    this string as an argument.  */
 #define TREE_SYMBOL_REFERENCED(NODE) \