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

Message ID 20230131141140.3610133-2-qing.zhao@oracle.com
State New
Headers
Series PR101832: Handle component_ref to a structure/union field including flexible array member for builtin_object_size |

Commit Message

Qing Zhao Jan. 31, 2023, 2:11 p.m. UTC
  GCC extension accepts the case when a struct with a flexible array member
is embedded into another struct (possibly recursively).
__builtin_object_size should treat such struct as flexible size per
-fstrict-flex-arrays.

	PR tree-optimization/101832

gcc/ChangeLog:

	PR tree-optimization/101832
	* tree-object-size.cc (flexible_size_type_p): New function.
	(addr_object_size): Handle structure/union type when it has
	flexible size.

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.c: New test.
---
 .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
 .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
 gcc/tree-object-size.cc                       | 115 +++++++++++----
 5 files changed, 611 insertions(+), 28 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.c
  

Comments

Richard Biener Feb. 1, 2023, 11:41 a.m. UTC | #1
On Tue, 31 Jan 2023, Qing Zhao wrote:

> GCC extension accepts the case when a struct with a flexible array member
> is embedded into another struct (possibly recursively).
> __builtin_object_size should treat such struct as flexible size per
> -fstrict-flex-arrays.
> 
> 	PR tree-optimization/101832
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/101832
> 	* tree-object-size.cc (flexible_size_type_p): New function.
> 	(addr_object_size): Handle structure/union type when it has
> 	flexible size.
> 
> 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.c: New test.
> ---
>  .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
>  .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
>  .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
>  .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
>  gcc/tree-object-size.cc                       | 115 +++++++++++----
>  5 files changed, 611 insertions(+), 28 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.c
> 
> 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..f38babc5415
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> @@ -0,0 +1,135 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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), -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));
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    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..aaae99b8d67
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> @@ -0,0 +1,135 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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));
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    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..424264e2acd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> @@ -0,0 +1,135 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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), 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));
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    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..8ed6980edf0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> @@ -0,0 +1,119 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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 = (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);
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    return 0;
> +}
> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
> index 9a936a91983..56b78ca2a8c 100644
> --- a/gcc/tree-object-size.cc
> +++ b/gcc/tree-object-size.cc
> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
>    return size;
>  }
>  
> +/* Determine whether TYPE is a structure with a flexible array member
> +   per -fstrict-flex-array or a union containing such a structure
> +   (possibly recursively).  */
> +static bool
> +flexible_size_type_p (const_tree type)
> +{
> +  tree x = NULL_TREE;
> +  tree last = NULL_TREE;
> +  switch (TREE_CODE (type))
> +    {
> +    case RECORD_TYPE:
> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> +	if (TREE_CODE (x) == FIELD_DECL)
> +	  last = x;
> +      if (last == NULL_TREE)
> +	return false;
> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
> +	  && !DECL_NOT_FLEXARRAY (last))
> +	return true;
> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
> +	return flexible_size_type_p (TREE_TYPE (last));

For types with many members this can become quite slow (IIRC we had
bugs about similar walks of all fields in types), and this function
looks like it's invoked multiple times on the same type per TU.

In principle the property is fixed at the time we lay out a record
type, so we might want to compute it at that time and record the
result.

> +      return false;
> +    case UNION_TYPE:
> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> +	{
> +	  if (TREE_CODE (x) == FIELD_DECL
> +	      && flexible_array_type_p (TREE_TYPE (x)))
> +	    return true;
> +	}
> +      return false;
> +    default:
> +      return false;
> +  }
> +}
> +
>  /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>     OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>     If unknown, return size_unknown (object_size_type).  */
> @@ -633,45 +669,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 the record or union does not have flexible size
> +		       compute the object size directly.  */
> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>  		      {
> -			/* compute object size only if v is not a
> -			   flexible array member.  */
> -			if (!is_flexible_array_mem_ref)
> +			if (!flexible_size_type_p (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;
>
  
Qing Zhao Feb. 1, 2023, 2:19 p.m. UTC | #2
> On Feb 1, 2023, at 6:41 AM, Richard Biener <rguenther@suse.de> wrote:
> 
> On Tue, 31 Jan 2023, Qing Zhao wrote:
> 
>> GCC extension accepts the case when a struct with a flexible array member
>> is embedded into another struct (possibly recursively).
>> __builtin_object_size should treat such struct as flexible size per
>> -fstrict-flex-arrays.
>> 
>> 	PR tree-optimization/101832
>> 
>> gcc/ChangeLog:
>> 
>> 	PR tree-optimization/101832
>> 	* tree-object-size.cc (flexible_size_type_p): New function.
>> 	(addr_object_size): Handle structure/union type when it has
>> 	flexible size.
>> 
>> 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.c: New test.
>> ---
>> .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
>> .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
>> .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
>> .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
>> gcc/tree-object-size.cc                       | 115 +++++++++++----
>> 5 files changed, 611 insertions(+), 28 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.c
>> 
>> 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..f38babc5415
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
>> @@ -0,0 +1,135 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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), -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));
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    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..aaae99b8d67
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
>> @@ -0,0 +1,135 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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));
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    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..424264e2acd
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
>> @@ -0,0 +1,135 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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), 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));
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    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..8ed6980edf0
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
>> @@ -0,0 +1,119 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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 = (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);
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    return 0;
>> +}
>> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
>> index 9a936a91983..56b78ca2a8c 100644
>> --- a/gcc/tree-object-size.cc
>> +++ b/gcc/tree-object-size.cc
>> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
>>   return size;
>> }
>> 
>> +/* Determine whether TYPE is a structure with a flexible array member
>> +   per -fstrict-flex-array or a union containing such a structure
>> +   (possibly recursively).  */
>> +static bool
>> +flexible_size_type_p (const_tree type)
>> +{
>> +  tree x = NULL_TREE;
>> +  tree last = NULL_TREE;
>> +  switch (TREE_CODE (type))
>> +    {
>> +    case RECORD_TYPE:
>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>> +	if (TREE_CODE (x) == FIELD_DECL)
>> +	  last = x;
>> +      if (last == NULL_TREE)
>> +	return false;
>> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
>> +	  && !DECL_NOT_FLEXARRAY (last))
>> +	return true;
>> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
>> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
>> +	return flexible_size_type_p (TREE_TYPE (last));
> 
> For types with many members this can become quite slow (IIRC we had
> bugs about similar walks of all fields in types), and this function
> looks like it's invoked multiple times on the same type per TU.
> 
> In principle the property is fixed at the time we lay out a record
> type, so we might want to compute it at that time and record the
> result.

You mean in FE? 

Yes, that’s better and cleaner. 

I will add one more field in the TYPE structure to record this information and check this field during middle end.

I had the same thought in the beginning, but not sure whether adding a new field in IR is necessary or not, other places in middle end might not use this new field. 

thanks.

Qing

> 
>> +      return false;
>> +    case UNION_TYPE:
>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>> +	{
>> +	  if (TREE_CODE (x) == FIELD_DECL
>> +	      && flexible_array_type_p (TREE_TYPE (x)))
>> +	    return true;
>> +	}
>> +      return false;
>> +    default:
>> +      return false;
>> +  }
>> +}
>> +
>> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>>    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>>    If unknown, return size_unknown (object_size_type).  */
>> @@ -633,45 +669,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 the record or union does not have flexible size
>> +		       compute the object size directly.  */
>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>> 		      {
>> -			/* compute object size only if v is not a
>> -			   flexible array member.  */
>> -			if (!is_flexible_array_mem_ref)
>> +			if (!flexible_size_type_p (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;
>> 
> 
> -- 
> Richard Biener <rguenther@suse.de>
> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> HRB 36809 (AG Nuernberg)
  
Siddhesh Poyarekar Feb. 1, 2023, 4:48 p.m. UTC | #3
On 2023-01-31 09:11, Qing Zhao wrote:
> GCC extension accepts the case when a struct with a flexible array member
> is embedded into another struct (possibly recursively).
> __builtin_object_size should treat such struct as flexible size per
> -fstrict-flex-arrays.
> 
> 	PR tree-optimization/101832
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/101832
> 	* tree-object-size.cc (flexible_size_type_p): New function.
> 	(addr_object_size): Handle structure/union type when it has
> 	flexible size.
> 
> 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.c: New test.
> ---
>   .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
>   .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
>   .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
>   .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
>   gcc/tree-object-size.cc                       | 115 +++++++++++----
>   5 files changed, 611 insertions(+), 28 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.c
> 
> 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..f38babc5415
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> @@ -0,0 +1,135 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \

I just pushed my testsuite fix, so you could use the macros in 
gcc.dg/builtin-object-size-common.h instead of accounting this yourself.

Also if you use __builtin_printf, you won't have to include stdio.h.

Thanks,
Sid

> +  } \
> +} 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), -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));
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    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..aaae99b8d67
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> @@ -0,0 +1,135 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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));
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    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..424264e2acd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> @@ -0,0 +1,135 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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), 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));
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    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..8ed6980edf0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> @@ -0,0 +1,119 @@
> +/* 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 <stdio.h>
> +
> +unsigned n_fails = 0;
> +
> +#define expect(p, _v) do { \
> +  size_t v = _v; \
> +  if (p == v) \
> +    printf("ok:  %s == %zd\n", #p, p); \
> +  else {\
> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> +    n_fails++; \
> +  } \
> +} 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 = (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);
> +
> +    if (n_fails > 0)
> +      __builtin_abort ();
> +
> +    return 0;
> +}
> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
> index 9a936a91983..56b78ca2a8c 100644
> --- a/gcc/tree-object-size.cc
> +++ b/gcc/tree-object-size.cc
> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
>     return size;
>   }
>   
> +/* Determine whether TYPE is a structure with a flexible array member
> +   per -fstrict-flex-array or a union containing such a structure
> +   (possibly recursively).  */
> +static bool
> +flexible_size_type_p (const_tree type)
> +{
> +  tree x = NULL_TREE;
> +  tree last = NULL_TREE;
> +  switch (TREE_CODE (type))
> +    {
> +    case RECORD_TYPE:
> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> +	if (TREE_CODE (x) == FIELD_DECL)
> +	  last = x;
> +      if (last == NULL_TREE)
> +	return false;
> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
> +	  && !DECL_NOT_FLEXARRAY (last))
> +	return true;
> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
> +	return flexible_size_type_p (TREE_TYPE (last));
> +      return false;
> +    case UNION_TYPE:
> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> +	{
> +	  if (TREE_CODE (x) == FIELD_DECL
> +	      && flexible_array_type_p (TREE_TYPE (x)))
> +	    return true;
> +	}
> +      return false;
> +    default:
> +      return false;
> +  }
> +}
> +
>   /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>      OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>      If unknown, return size_unknown (object_size_type).  */
> @@ -633,45 +669,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 the record or union does not have flexible size
> +		       compute the object size directly.  */
> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>   		      {
> -			/* compute object size only if v is not a
> -			   flexible array member.  */
> -			if (!is_flexible_array_mem_ref)
> +			if (!flexible_size_type_p (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;
  
Qing Zhao Feb. 1, 2023, 6:20 p.m. UTC | #4
Siddhesh,

Thanks. I will update the testing case per your change.

Qing

> On Feb 1, 2023, at 11:48 AM, Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
> 
> On 2023-01-31 09:11, Qing Zhao wrote:
>> GCC extension accepts the case when a struct with a flexible array member
>> is embedded into another struct (possibly recursively).
>> __builtin_object_size should treat such struct as flexible size per
>> -fstrict-flex-arrays.
>> 	PR tree-optimization/101832
>> gcc/ChangeLog:
>> 	PR tree-optimization/101832
>> 	* tree-object-size.cc (flexible_size_type_p): New function.
>> 	(addr_object_size): Handle structure/union type when it has
>> 	flexible size.
>> 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.c: New test.
>> ---
>>  .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
>>  .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
>>  .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
>>  .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
>>  gcc/tree-object-size.cc                       | 115 +++++++++++----
>>  5 files changed, 611 insertions(+), 28 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.c
>> 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..f38babc5415
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
>> @@ -0,0 +1,135 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
> 
> I just pushed my testsuite fix, so you could use the macros in gcc.dg/builtin-object-size-common.h instead of accounting this yourself.
> 
> Also if you use __builtin_printf, you won't have to include stdio.h.
> 
> Thanks,
> Sid
> 
>> +  } \
>> +} 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), -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));
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    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..aaae99b8d67
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
>> @@ -0,0 +1,135 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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));
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    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..424264e2acd
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
>> @@ -0,0 +1,135 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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), 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));
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    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..8ed6980edf0
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
>> @@ -0,0 +1,119 @@
>> +/* 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 <stdio.h>
>> +
>> +unsigned n_fails = 0;
>> +
>> +#define expect(p, _v) do { \
>> +  size_t v = _v; \
>> +  if (p == v) \
>> +    printf("ok:  %s == %zd\n", #p, p); \
>> +  else {\
>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>> +    n_fails++; \
>> +  } \
>> +} 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 = (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);
>> +
>> +    if (n_fails > 0)
>> +      __builtin_abort ();
>> +
>> +    return 0;
>> +}
>> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
>> index 9a936a91983..56b78ca2a8c 100644
>> --- a/gcc/tree-object-size.cc
>> +++ b/gcc/tree-object-size.cc
>> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
>>    return size;
>>  }
>>  +/* Determine whether TYPE is a structure with a flexible array member
>> +   per -fstrict-flex-array or a union containing such a structure
>> +   (possibly recursively).  */
>> +static bool
>> +flexible_size_type_p (const_tree type)
>> +{
>> +  tree x = NULL_TREE;
>> +  tree last = NULL_TREE;
>> +  switch (TREE_CODE (type))
>> +    {
>> +    case RECORD_TYPE:
>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>> +	if (TREE_CODE (x) == FIELD_DECL)
>> +	  last = x;
>> +      if (last == NULL_TREE)
>> +	return false;
>> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
>> +	  && !DECL_NOT_FLEXARRAY (last))
>> +	return true;
>> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
>> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
>> +	return flexible_size_type_p (TREE_TYPE (last));
>> +      return false;
>> +    case UNION_TYPE:
>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>> +	{
>> +	  if (TREE_CODE (x) == FIELD_DECL
>> +	      && flexible_array_type_p (TREE_TYPE (x)))
>> +	    return true;
>> +	}
>> +      return false;
>> +    default:
>> +      return false;
>> +  }
>> +}
>> +
>>  /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>>     OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>>     If unknown, return size_unknown (object_size_type).  */
>> @@ -633,45 +669,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 the record or union does not have flexible size
>> +		       compute the object size directly.  */
>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>>  		      {
>> -			/* compute object size only if v is not a
>> -			   flexible array member.  */
>> -			if (!is_flexible_array_mem_ref)
>> +			if (!flexible_size_type_p (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;
  
Richard Biener Feb. 2, 2023, 8:07 a.m. UTC | #5
On Wed, 1 Feb 2023, Qing Zhao wrote:

> 
> 
> > On Feb 1, 2023, at 6:41 AM, Richard Biener <rguenther@suse.de> wrote:
> > 
> > On Tue, 31 Jan 2023, Qing Zhao wrote:
> > 
> >> GCC extension accepts the case when a struct with a flexible array member
> >> is embedded into another struct (possibly recursively).
> >> __builtin_object_size should treat such struct as flexible size per
> >> -fstrict-flex-arrays.
> >> 
> >> 	PR tree-optimization/101832
> >> 
> >> gcc/ChangeLog:
> >> 
> >> 	PR tree-optimization/101832
> >> 	* tree-object-size.cc (flexible_size_type_p): New function.
> >> 	(addr_object_size): Handle structure/union type when it has
> >> 	flexible size.
> >> 
> >> 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.c: New test.
> >> ---
> >> .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
> >> .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
> >> .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
> >> .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
> >> gcc/tree-object-size.cc                       | 115 +++++++++++----
> >> 5 files changed, 611 insertions(+), 28 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.c
> >> 
> >> 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..f38babc5415
> >> --- /dev/null
> >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> >> @@ -0,0 +1,135 @@
> >> +/* 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 <stdio.h>
> >> +
> >> +unsigned n_fails = 0;
> >> +
> >> +#define expect(p, _v) do { \
> >> +  size_t v = _v; \
> >> +  if (p == v) \
> >> +    printf("ok:  %s == %zd\n", #p, p); \
> >> +  else {\
> >> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >> +    n_fails++; \
> >> +  } \
> >> +} 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), -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));
> >> +
> >> +    if (n_fails > 0)
> >> +      __builtin_abort ();
> >> +
> >> +    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..aaae99b8d67
> >> --- /dev/null
> >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> >> @@ -0,0 +1,135 @@
> >> +/* 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 <stdio.h>
> >> +
> >> +unsigned n_fails = 0;
> >> +
> >> +#define expect(p, _v) do { \
> >> +  size_t v = _v; \
> >> +  if (p == v) \
> >> +    printf("ok:  %s == %zd\n", #p, p); \
> >> +  else {\
> >> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >> +    n_fails++; \
> >> +  } \
> >> +} 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));
> >> +
> >> +    if (n_fails > 0)
> >> +      __builtin_abort ();
> >> +
> >> +    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..424264e2acd
> >> --- /dev/null
> >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> >> @@ -0,0 +1,135 @@
> >> +/* 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 <stdio.h>
> >> +
> >> +unsigned n_fails = 0;
> >> +
> >> +#define expect(p, _v) do { \
> >> +  size_t v = _v; \
> >> +  if (p == v) \
> >> +    printf("ok:  %s == %zd\n", #p, p); \
> >> +  else {\
> >> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >> +    n_fails++; \
> >> +  } \
> >> +} 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), 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));
> >> +
> >> +    if (n_fails > 0)
> >> +      __builtin_abort ();
> >> +
> >> +    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..8ed6980edf0
> >> --- /dev/null
> >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> >> @@ -0,0 +1,119 @@
> >> +/* 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 <stdio.h>
> >> +
> >> +unsigned n_fails = 0;
> >> +
> >> +#define expect(p, _v) do { \
> >> +  size_t v = _v; \
> >> +  if (p == v) \
> >> +    printf("ok:  %s == %zd\n", #p, p); \
> >> +  else {\
> >> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >> +    n_fails++; \
> >> +  } \
> >> +} 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 = (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);
> >> +
> >> +    if (n_fails > 0)
> >> +      __builtin_abort ();
> >> +
> >> +    return 0;
> >> +}
> >> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
> >> index 9a936a91983..56b78ca2a8c 100644
> >> --- a/gcc/tree-object-size.cc
> >> +++ b/gcc/tree-object-size.cc
> >> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
> >>   return size;
> >> }
> >> 
> >> +/* Determine whether TYPE is a structure with a flexible array member
> >> +   per -fstrict-flex-array or a union containing such a structure
> >> +   (possibly recursively).  */
> >> +static bool
> >> +flexible_size_type_p (const_tree type)
> >> +{
> >> +  tree x = NULL_TREE;
> >> +  tree last = NULL_TREE;
> >> +  switch (TREE_CODE (type))
> >> +    {
> >> +    case RECORD_TYPE:
> >> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> >> +	if (TREE_CODE (x) == FIELD_DECL)
> >> +	  last = x;
> >> +      if (last == NULL_TREE)
> >> +	return false;
> >> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
> >> +	  && !DECL_NOT_FLEXARRAY (last))
> >> +	return true;
> >> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
> >> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
> >> +	return flexible_size_type_p (TREE_TYPE (last));
> > 
> > For types with many members this can become quite slow (IIRC we had
> > bugs about similar walks of all fields in types), and this function
> > looks like it's invoked multiple times on the same type per TU.
> > 
> > In principle the property is fixed at the time we lay out a record
> > type, so we might want to compute it at that time and record the
> > result.
> 
> You mean in FE? 

Yes, either in the frontend or in the middle-ends layout_type.

> Yes, that?s better and cleaner.
> 
> I will add one more field in the TYPE structure to record this information and check this field during middle end.
> 
> I had the same thought in the beginning, but not sure whether adding a 
> new field in IR is necessary or not, other places in middle end might 
> not use this new field.

It might be interesting to search for other code walking all fields of
a type to determine this or similar info.

> thanks.
> 
> Qing
> 
> > 
> >> +      return false;
> >> +    case UNION_TYPE:
> >> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> >> +	{
> >> +	  if (TREE_CODE (x) == FIELD_DECL
> >> +	      && flexible_array_type_p (TREE_TYPE (x)))
> >> +	    return true;
> >> +	}
> >> +      return false;
> >> +    default:
> >> +      return false;
> >> +  }
> >> +}
> >> +
> >> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
> >>    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
> >>    If unknown, return size_unknown (object_size_type).  */
> >> @@ -633,45 +669,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 the record or union does not have flexible size
> >> +		       compute the object size directly.  */
> >> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
> >> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
> >> 		      {
> >> -			/* compute object size only if v is not a
> >> -			   flexible array member.  */
> >> -			if (!is_flexible_array_mem_ref)
> >> +			if (!flexible_size_type_p (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;
> >> 
> > 
> > -- 
> > Richard Biener <rguenther@suse.de>
> > SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> > Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> > HRB 36809 (AG Nuernberg)
> 
>
  
Qing Zhao Feb. 2, 2023, 1:52 p.m. UTC | #6
> On Feb 2, 2023, at 3:07 AM, Richard Biener <rguenther@suse.de> wrote:
> 
> On Wed, 1 Feb 2023, Qing Zhao wrote:
> 
>> 
>> 
>>> On Feb 1, 2023, at 6:41 AM, Richard Biener <rguenther@suse.de> wrote:
>>> 
>>> On Tue, 31 Jan 2023, Qing Zhao wrote:
>>> 
>>>> GCC extension accepts the case when a struct with a flexible array member
>>>> is embedded into another struct (possibly recursively).
>>>> __builtin_object_size should treat such struct as flexible size per
>>>> -fstrict-flex-arrays.
>>>> 
>>>> 	PR tree-optimization/101832
>>>> 
>>>> gcc/ChangeLog:
>>>> 
>>>> 	PR tree-optimization/101832
>>>> 	* tree-object-size.cc (flexible_size_type_p): New function.
>>>> 	(addr_object_size): Handle structure/union type when it has
>>>> 	flexible size.
>>>> 
>>>> 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.c: New test.
>>>> ---
>>>> .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
>>>> .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
>>>> .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
>>>> .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
>>>> gcc/tree-object-size.cc                       | 115 +++++++++++----
>>>> 5 files changed, 611 insertions(+), 28 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.c
>>>> 
>>>> 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..f38babc5415
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
>>>> @@ -0,0 +1,135 @@
>>>> +/* 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 <stdio.h>
>>>> +
>>>> +unsigned n_fails = 0;
>>>> +
>>>> +#define expect(p, _v) do { \
>>>> +  size_t v = _v; \
>>>> +  if (p == v) \
>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>> +  else {\
>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>> +    n_fails++; \
>>>> +  } \
>>>> +} 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), -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));
>>>> +
>>>> +    if (n_fails > 0)
>>>> +      __builtin_abort ();
>>>> +
>>>> +    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..aaae99b8d67
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
>>>> @@ -0,0 +1,135 @@
>>>> +/* 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 <stdio.h>
>>>> +
>>>> +unsigned n_fails = 0;
>>>> +
>>>> +#define expect(p, _v) do { \
>>>> +  size_t v = _v; \
>>>> +  if (p == v) \
>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>> +  else {\
>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>> +    n_fails++; \
>>>> +  } \
>>>> +} 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));
>>>> +
>>>> +    if (n_fails > 0)
>>>> +      __builtin_abort ();
>>>> +
>>>> +    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..424264e2acd
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
>>>> @@ -0,0 +1,135 @@
>>>> +/* 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 <stdio.h>
>>>> +
>>>> +unsigned n_fails = 0;
>>>> +
>>>> +#define expect(p, _v) do { \
>>>> +  size_t v = _v; \
>>>> +  if (p == v) \
>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>> +  else {\
>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>> +    n_fails++; \
>>>> +  } \
>>>> +} 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), 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));
>>>> +
>>>> +    if (n_fails > 0)
>>>> +      __builtin_abort ();
>>>> +
>>>> +    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..8ed6980edf0
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
>>>> @@ -0,0 +1,119 @@
>>>> +/* 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 <stdio.h>
>>>> +
>>>> +unsigned n_fails = 0;
>>>> +
>>>> +#define expect(p, _v) do { \
>>>> +  size_t v = _v; \
>>>> +  if (p == v) \
>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>> +  else {\
>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>> +    n_fails++; \
>>>> +  } \
>>>> +} 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 = (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);
>>>> +
>>>> +    if (n_fails > 0)
>>>> +      __builtin_abort ();
>>>> +
>>>> +    return 0;
>>>> +}
>>>> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
>>>> index 9a936a91983..56b78ca2a8c 100644
>>>> --- a/gcc/tree-object-size.cc
>>>> +++ b/gcc/tree-object-size.cc
>>>> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
>>>>  return size;
>>>> }
>>>> 
>>>> +/* Determine whether TYPE is a structure with a flexible array member
>>>> +   per -fstrict-flex-array or a union containing such a structure
>>>> +   (possibly recursively).  */
>>>> +static bool
>>>> +flexible_size_type_p (const_tree type)
>>>> +{
>>>> +  tree x = NULL_TREE;
>>>> +  tree last = NULL_TREE;
>>>> +  switch (TREE_CODE (type))
>>>> +    {
>>>> +    case RECORD_TYPE:
>>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>>>> +	if (TREE_CODE (x) == FIELD_DECL)
>>>> +	  last = x;
>>>> +      if (last == NULL_TREE)
>>>> +	return false;
>>>> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
>>>> +	  && !DECL_NOT_FLEXARRAY (last))
>>>> +	return true;
>>>> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
>>>> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
>>>> +	return flexible_size_type_p (TREE_TYPE (last));
>>> 
>>> For types with many members this can become quite slow (IIRC we had
>>> bugs about similar walks of all fields in types), and this function
>>> looks like it's invoked multiple times on the same type per TU.
>>> 
>>> In principle the property is fixed at the time we lay out a record
>>> type, so we might want to compute it at that time and record the
>>> result.
>> 
>> You mean in FE? 
> 
> Yes, either in the frontend or in the middle-ends layout_type.
> 
>> Yes, that?s better and cleaner.
>> 
>> I will add one more field in the TYPE structure to record this information and check this field during middle end.
>> 
>> I had the same thought in the beginning, but not sure whether adding a 
>> new field in IR is necessary or not, other places in middle end might 
>> not use this new field.
> 
> It might be interesting to search for other code walking all fields of
> a type to determine this or similar info.

There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:

/* Determine whether TYPE is a structure with a flexible array member,
   or a union containing such a structure (possibly recursively).  */
flexible_array_type_p

However, this routine is a little different than the one I tried to add:

In the current routine “flexible_array_type_p”,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.

So, my question is:  shall we accept multiple nesting in structure? i.e.

struct A {
  int n;
  char data[];/* Content following header */
};

struct B {
  int m;
  struct A a;
};

struct C {
  int q;
  struct B b;
};

Qing
> 
>> thanks.
>> 
>> Qing
>> 
>>> 
>>>> +      return false;
>>>> +    case UNION_TYPE:
>>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>>>> +	{
>>>> +	  if (TREE_CODE (x) == FIELD_DECL
>>>> +	      && flexible_array_type_p (TREE_TYPE (x)))
>>>> +	    return true;
>>>> +	}
>>>> +      return false;
>>>> +    default:
>>>> +      return false;
>>>> +  }
>>>> +}
>>>> +
>>>> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>>>>   OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>>>>   If unknown, return size_unknown (object_size_type).  */
>>>> @@ -633,45 +669,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 the record or union does not have flexible size
>>>> +		       compute the object size directly.  */
>>>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
>>>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>>>> 		      {
>>>> -			/* compute object size only if v is not a
>>>> -			   flexible array member.  */
>>>> -			if (!is_flexible_array_mem_ref)
>>>> +			if (!flexible_size_type_p (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;
>>>> 
>>> 
>>> -- 
>>> Richard Biener <rguenther@suse.de>
>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
>>> HRB 36809 (AG Nuernberg)
>> 
>> 
> 
> -- 
> Richard Biener <rguenther@suse.de>
> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> HRB 36809 (AG Nuernberg)
  
Richard Biener Feb. 2, 2023, 1:54 p.m. UTC | #7
On Thu, 2 Feb 2023, Qing Zhao wrote:

> 
> 
> > On Feb 2, 2023, at 3:07 AM, Richard Biener <rguenther@suse.de> wrote:
> > 
> > On Wed, 1 Feb 2023, Qing Zhao wrote:
> > 
> >> 
> >> 
> >>> On Feb 1, 2023, at 6:41 AM, Richard Biener <rguenther@suse.de> wrote:
> >>> 
> >>> On Tue, 31 Jan 2023, Qing Zhao wrote:
> >>> 
> >>>> GCC extension accepts the case when a struct with a flexible array member
> >>>> is embedded into another struct (possibly recursively).
> >>>> __builtin_object_size should treat such struct as flexible size per
> >>>> -fstrict-flex-arrays.
> >>>> 
> >>>> 	PR tree-optimization/101832
> >>>> 
> >>>> gcc/ChangeLog:
> >>>> 
> >>>> 	PR tree-optimization/101832
> >>>> 	* tree-object-size.cc (flexible_size_type_p): New function.
> >>>> 	(addr_object_size): Handle structure/union type when it has
> >>>> 	flexible size.
> >>>> 
> >>>> 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.c: New test.
> >>>> ---
> >>>> .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
> >>>> .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
> >>>> .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
> >>>> .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
> >>>> gcc/tree-object-size.cc                       | 115 +++++++++++----
> >>>> 5 files changed, 611 insertions(+), 28 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.c
> >>>> 
> >>>> 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..f38babc5415
> >>>> --- /dev/null
> >>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
> >>>> @@ -0,0 +1,135 @@
> >>>> +/* 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 <stdio.h>
> >>>> +
> >>>> +unsigned n_fails = 0;
> >>>> +
> >>>> +#define expect(p, _v) do { \
> >>>> +  size_t v = _v; \
> >>>> +  if (p == v) \
> >>>> +    printf("ok:  %s == %zd\n", #p, p); \
> >>>> +  else {\
> >>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >>>> +    n_fails++; \
> >>>> +  } \
> >>>> +} 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), -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));
> >>>> +
> >>>> +    if (n_fails > 0)
> >>>> +      __builtin_abort ();
> >>>> +
> >>>> +    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..aaae99b8d67
> >>>> --- /dev/null
> >>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
> >>>> @@ -0,0 +1,135 @@
> >>>> +/* 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 <stdio.h>
> >>>> +
> >>>> +unsigned n_fails = 0;
> >>>> +
> >>>> +#define expect(p, _v) do { \
> >>>> +  size_t v = _v; \
> >>>> +  if (p == v) \
> >>>> +    printf("ok:  %s == %zd\n", #p, p); \
> >>>> +  else {\
> >>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >>>> +    n_fails++; \
> >>>> +  } \
> >>>> +} 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));
> >>>> +
> >>>> +    if (n_fails > 0)
> >>>> +      __builtin_abort ();
> >>>> +
> >>>> +    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..424264e2acd
> >>>> --- /dev/null
> >>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
> >>>> @@ -0,0 +1,135 @@
> >>>> +/* 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 <stdio.h>
> >>>> +
> >>>> +unsigned n_fails = 0;
> >>>> +
> >>>> +#define expect(p, _v) do { \
> >>>> +  size_t v = _v; \
> >>>> +  if (p == v) \
> >>>> +    printf("ok:  %s == %zd\n", #p, p); \
> >>>> +  else {\
> >>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >>>> +    n_fails++; \
> >>>> +  } \
> >>>> +} 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), 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));
> >>>> +
> >>>> +    if (n_fails > 0)
> >>>> +      __builtin_abort ();
> >>>> +
> >>>> +    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..8ed6980edf0
> >>>> --- /dev/null
> >>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
> >>>> @@ -0,0 +1,119 @@
> >>>> +/* 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 <stdio.h>
> >>>> +
> >>>> +unsigned n_fails = 0;
> >>>> +
> >>>> +#define expect(p, _v) do { \
> >>>> +  size_t v = _v; \
> >>>> +  if (p == v) \
> >>>> +    printf("ok:  %s == %zd\n", #p, p); \
> >>>> +  else {\
> >>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
> >>>> +    n_fails++; \
> >>>> +  } \
> >>>> +} 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 = (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);
> >>>> +
> >>>> +    if (n_fails > 0)
> >>>> +      __builtin_abort ();
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
> >>>> index 9a936a91983..56b78ca2a8c 100644
> >>>> --- a/gcc/tree-object-size.cc
> >>>> +++ b/gcc/tree-object-size.cc
> >>>> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
> >>>>  return size;
> >>>> }
> >>>> 
> >>>> +/* Determine whether TYPE is a structure with a flexible array member
> >>>> +   per -fstrict-flex-array or a union containing such a structure
> >>>> +   (possibly recursively).  */
> >>>> +static bool
> >>>> +flexible_size_type_p (const_tree type)
> >>>> +{
> >>>> +  tree x = NULL_TREE;
> >>>> +  tree last = NULL_TREE;
> >>>> +  switch (TREE_CODE (type))
> >>>> +    {
> >>>> +    case RECORD_TYPE:
> >>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> >>>> +	if (TREE_CODE (x) == FIELD_DECL)
> >>>> +	  last = x;
> >>>> +      if (last == NULL_TREE)
> >>>> +	return false;
> >>>> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
> >>>> +	  && !DECL_NOT_FLEXARRAY (last))
> >>>> +	return true;
> >>>> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
> >>>> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
> >>>> +	return flexible_size_type_p (TREE_TYPE (last));
> >>> 
> >>> For types with many members this can become quite slow (IIRC we had
> >>> bugs about similar walks of all fields in types), and this function
> >>> looks like it's invoked multiple times on the same type per TU.
> >>> 
> >>> In principle the property is fixed at the time we lay out a record
> >>> type, so we might want to compute it at that time and record the
> >>> result.
> >> 
> >> You mean in FE? 
> > 
> > Yes, either in the frontend or in the middle-ends layout_type.
> > 
> >> Yes, that?s better and cleaner.
> >> 
> >> I will add one more field in the TYPE structure to record this information and check this field during middle end.
> >> 
> >> I had the same thought in the beginning, but not sure whether adding a 
> >> new field in IR is necessary or not, other places in middle end might 
> >> not use this new field.
> > 
> > It might be interesting to search for other code walking all fields of
> > a type to determine this or similar info.
> 
> There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:
> 
> /* Determine whether TYPE is a structure with a flexible array member,
>    or a union containing such a structure (possibly recursively).  */
> flexible_array_type_p
> 
> However, this routine is a little different than the one I tried to add:
> 
> In the current routine ?flexible_array_type_p?,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.
> 
> So, my question is:  shall we accept multiple nesting in structure? i.e.

If we don't reject the testcase with an error, then yes.

> struct A {
>   int n;
>   char data[];/* Content following header */
> };
> 
> struct B {
>   int m;
>   struct A a;
> };
> 
> struct C {
>   int q;
>   struct B b;
> };
> 
> Qing
> > 
> >> thanks.
> >> 
> >> Qing
> >> 
> >>> 
> >>>> +      return false;
> >>>> +    case UNION_TYPE:
> >>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> >>>> +	{
> >>>> +	  if (TREE_CODE (x) == FIELD_DECL
> >>>> +	      && flexible_array_type_p (TREE_TYPE (x)))
> >>>> +	    return true;
> >>>> +	}
> >>>> +      return false;
> >>>> +    default:
> >>>> +      return false;
> >>>> +  }
> >>>> +}
> >>>> +
> >>>> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
> >>>>   OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
> >>>>   If unknown, return size_unknown (object_size_type).  */
> >>>> @@ -633,45 +669,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 the record or union does not have flexible size
> >>>> +		       compute the object size directly.  */
> >>>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
> >>>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
> >>>> 		      {
> >>>> -			/* compute object size only if v is not a
> >>>> -			   flexible array member.  */
> >>>> -			if (!is_flexible_array_mem_ref)
> >>>> +			if (!flexible_size_type_p (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;
> >>>> 
> >>> 
> >>> -- 
> >>> Richard Biener <rguenther@suse.de>
> >>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> >>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> >>> HRB 36809 (AG Nuernberg)
> >> 
> >> 
> > 
> > -- 
> > Richard Biener <rguenther@suse.de>
> > SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> > Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> > HRB 36809 (AG Nuernberg)
> 
>
  
Qing Zhao Feb. 2, 2023, 2:38 p.m. UTC | #8
> On Feb 2, 2023, at 8:54 AM, Richard Biener <rguenther@suse.de> wrote:
> 
> On Thu, 2 Feb 2023, Qing Zhao wrote:
> 
>> 
>> 
>>> On Feb 2, 2023, at 3:07 AM, Richard Biener <rguenther@suse.de> wrote:
>>> 
>>> On Wed, 1 Feb 2023, Qing Zhao wrote:
>>> 
>>>> 
>>>> 
>>>>> On Feb 1, 2023, at 6:41 AM, Richard Biener <rguenther@suse.de> wrote:
>>>>> 
>>>>> On Tue, 31 Jan 2023, Qing Zhao wrote:
>>>>> 
>>>>>> GCC extension accepts the case when a struct with a flexible array member
>>>>>> is embedded into another struct (possibly recursively).
>>>>>> __builtin_object_size should treat such struct as flexible size per
>>>>>> -fstrict-flex-arrays.
>>>>>> 
>>>>>> 	PR tree-optimization/101832
>>>>>> 
>>>>>> gcc/ChangeLog:
>>>>>> 
>>>>>> 	PR tree-optimization/101832
>>>>>> 	* tree-object-size.cc (flexible_size_type_p): New function.
>>>>>> 	(addr_object_size): Handle structure/union type when it has
>>>>>> 	flexible size.
>>>>>> 
>>>>>> 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.c: New test.
>>>>>> ---
>>>>>> .../gcc.dg/builtin-object-size-pr101832-2.c   | 135 ++++++++++++++++++
>>>>>> .../gcc.dg/builtin-object-size-pr101832-3.c   | 135 ++++++++++++++++++
>>>>>> .../gcc.dg/builtin-object-size-pr101832-4.c   | 135 ++++++++++++++++++
>>>>>> .../gcc.dg/builtin-object-size-pr101832.c     | 119 +++++++++++++++
>>>>>> gcc/tree-object-size.cc                       | 115 +++++++++++----
>>>>>> 5 files changed, 611 insertions(+), 28 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.c
>>>>>> 
>>>>>> 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..f38babc5415
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
>>>>>> @@ -0,0 +1,135 @@
>>>>>> +/* 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 <stdio.h>
>>>>>> +
>>>>>> +unsigned n_fails = 0;
>>>>>> +
>>>>>> +#define expect(p, _v) do { \
>>>>>> +  size_t v = _v; \
>>>>>> +  if (p == v) \
>>>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>>>> +  else {\
>>>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>>>> +    n_fails++; \
>>>>>> +  } \
>>>>>> +} 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), -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));
>>>>>> +
>>>>>> +    if (n_fails > 0)
>>>>>> +      __builtin_abort ();
>>>>>> +
>>>>>> +    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..aaae99b8d67
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
>>>>>> @@ -0,0 +1,135 @@
>>>>>> +/* 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 <stdio.h>
>>>>>> +
>>>>>> +unsigned n_fails = 0;
>>>>>> +
>>>>>> +#define expect(p, _v) do { \
>>>>>> +  size_t v = _v; \
>>>>>> +  if (p == v) \
>>>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>>>> +  else {\
>>>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>>>> +    n_fails++; \
>>>>>> +  } \
>>>>>> +} 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));
>>>>>> +
>>>>>> +    if (n_fails > 0)
>>>>>> +      __builtin_abort ();
>>>>>> +
>>>>>> +    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..424264e2acd
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
>>>>>> @@ -0,0 +1,135 @@
>>>>>> +/* 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 <stdio.h>
>>>>>> +
>>>>>> +unsigned n_fails = 0;
>>>>>> +
>>>>>> +#define expect(p, _v) do { \
>>>>>> +  size_t v = _v; \
>>>>>> +  if (p == v) \
>>>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>>>> +  else {\
>>>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>>>> +    n_fails++; \
>>>>>> +  } \
>>>>>> +} 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), 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));
>>>>>> +
>>>>>> +    if (n_fails > 0)
>>>>>> +      __builtin_abort ();
>>>>>> +
>>>>>> +    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..8ed6980edf0
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
>>>>>> @@ -0,0 +1,119 @@
>>>>>> +/* 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 <stdio.h>
>>>>>> +
>>>>>> +unsigned n_fails = 0;
>>>>>> +
>>>>>> +#define expect(p, _v) do { \
>>>>>> +  size_t v = _v; \
>>>>>> +  if (p == v) \
>>>>>> +    printf("ok:  %s == %zd\n", #p, p); \
>>>>>> +  else {\
>>>>>> +    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
>>>>>> +    n_fails++; \
>>>>>> +  } \
>>>>>> +} 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 = (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);
>>>>>> +
>>>>>> +    if (n_fails > 0)
>>>>>> +      __builtin_abort ();
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
>>>>>> index 9a936a91983..56b78ca2a8c 100644
>>>>>> --- a/gcc/tree-object-size.cc
>>>>>> +++ b/gcc/tree-object-size.cc
>>>>>> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min)
>>>>>> return size;
>>>>>> }
>>>>>> 
>>>>>> +/* Determine whether TYPE is a structure with a flexible array member
>>>>>> +   per -fstrict-flex-array or a union containing such a structure
>>>>>> +   (possibly recursively).  */
>>>>>> +static bool
>>>>>> +flexible_size_type_p (const_tree type)
>>>>>> +{
>>>>>> +  tree x = NULL_TREE;
>>>>>> +  tree last = NULL_TREE;
>>>>>> +  switch (TREE_CODE (type))
>>>>>> +    {
>>>>>> +    case RECORD_TYPE:
>>>>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>>>>>> +	if (TREE_CODE (x) == FIELD_DECL)
>>>>>> +	  last = x;
>>>>>> +      if (last == NULL_TREE)
>>>>>> +	return false;
>>>>>> +      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
>>>>>> +	  && !DECL_NOT_FLEXARRAY (last))
>>>>>> +	return true;
>>>>>> +      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
>>>>>> +	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
>>>>>> +	return flexible_size_type_p (TREE_TYPE (last));
>>>>> 
>>>>> For types with many members this can become quite slow (IIRC we had
>>>>> bugs about similar walks of all fields in types), and this function
>>>>> looks like it's invoked multiple times on the same type per TU.
>>>>> 
>>>>> In principle the property is fixed at the time we lay out a record
>>>>> type, so we might want to compute it at that time and record the
>>>>> result.
>>>> 
>>>> You mean in FE? 
>>> 
>>> Yes, either in the frontend or in the middle-ends layout_type.
>>> 
>>>> Yes, that?s better and cleaner.
>>>> 
>>>> I will add one more field in the TYPE structure to record this information and check this field during middle end.
>>>> 
>>>> I had the same thought in the beginning, but not sure whether adding a 
>>>> new field in IR is necessary or not, other places in middle end might 
>>>> not use this new field.
>>> 
>>> It might be interesting to search for other code walking all fields of
>>> a type to determine this or similar info.
>> 
>> There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:
>> 
>> /* Determine whether TYPE is a structure with a flexible array member,
>>   or a union containing such a structure (possibly recursively).  */
>> flexible_array_type_p
>> 
>> However, this routine is a little different than the one I tried to add:
>> 
>> In the current routine ?flexible_array_type_p?,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.
>> 
>> So, my question is:  shall we accept multiple nesting in structure? i.e.
> 
> If we don't reject the testcase with an error, then yes.

Gcc currently accepts the multiple nesting in structure without error.  So, we will continue to accept such extension as long as the flex array is at the end of the structure.
At the same time, for the case the flex array is in the middle of the structure, issue additional warnings now to discourage such usage, and deprecate this case in a future release. 

Does this sound reasonable? 

Qing
> 
>> struct A {
>>  int n;
>>  char data[];/* Content following header */
>> };
>> 
>> struct B {
>>  int m;
>>  struct A a;
>> };
>> 
>> struct C {
>>  int q;
>>  struct B b;
>> };
>> 
>> Qing
>>> 
>>>> thanks.
>>>> 
>>>> Qing
>>>> 
>>>>> 
>>>>>> +      return false;
>>>>>> +    case UNION_TYPE:
>>>>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>>>>>> +	{
>>>>>> +	  if (TREE_CODE (x) == FIELD_DECL
>>>>>> +	      && flexible_array_type_p (TREE_TYPE (x)))
>>>>>> +	    return true;
>>>>>> +	}
>>>>>> +      return false;
>>>>>> +    default:
>>>>>> +      return false;
>>>>>> +  }
>>>>>> +}
>>>>>> +
>>>>>> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>>>>>>  OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>>>>>>  If unknown, return size_unknown (object_size_type).  */
>>>>>> @@ -633,45 +669,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 the record or union does not have flexible size
>>>>>> +		       compute the object size directly.  */
>>>>>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
>>>>>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>>>>>> 		      {
>>>>>> -			/* compute object size only if v is not a
>>>>>> -			   flexible array member.  */
>>>>>> -			if (!is_flexible_array_mem_ref)
>>>>>> +			if (!flexible_size_type_p (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;
>>>>>> 
>>>>> 
>>>>> -- 
>>>>> Richard Biener <rguenther@suse.de>
>>>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
>>>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
>>>>> HRB 36809 (AG Nuernberg)
>>>> 
>>>> 
>>> 
>>> -- 
>>> Richard Biener <rguenther@suse.de>
>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
>>> HRB 36809 (AG Nuernberg)
>> 
>> 
> 
> -- 
> Richard Biener <rguenther@suse.de>
> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> HRB 36809 (AG Nuernberg)
  
Richard Biener Feb. 3, 2023, 7:49 a.m. UTC | #9
On Thu, 2 Feb 2023, Qing Zhao wrote:

> 
> 
> > On Feb 2, 2023, at 8:54 AM, Richard Biener <rguenther@suse.de> wrote:
> > 
> > On Thu, 2 Feb 2023, Qing Zhao wrote:
> > 
> >> 
> >> 

[...]

> >>>>>> +	return flexible_size_type_p (TREE_TYPE (last));
> >>>>> 
> >>>>> For types with many members this can become quite slow (IIRC we had
> >>>>> bugs about similar walks of all fields in types), and this function
> >>>>> looks like it's invoked multiple times on the same type per TU.
> >>>>> 
> >>>>> In principle the property is fixed at the time we lay out a record
> >>>>> type, so we might want to compute it at that time and record the
> >>>>> result.
> >>>> 
> >>>> You mean in FE? 
> >>> 
> >>> Yes, either in the frontend or in the middle-ends layout_type.
> >>> 
> >>>> Yes, that?s better and cleaner.
> >>>> 
> >>>> I will add one more field in the TYPE structure to record this information and check this field during middle end.
> >>>> 
> >>>> I had the same thought in the beginning, but not sure whether adding a 
> >>>> new field in IR is necessary or not, other places in middle end might 
> >>>> not use this new field.
> >>> 
> >>> It might be interesting to search for other code walking all fields of
> >>> a type to determine this or similar info.
> >> 
> >> There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:
> >> 
> >> /* Determine whether TYPE is a structure with a flexible array member,
> >>   or a union containing such a structure (possibly recursively).  */
> >> flexible_array_type_p
> >> 
> >> However, this routine is a little different than the one I tried to add:
> >> 
> >> In the current routine ?flexible_array_type_p?,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.
> >> 
> >> So, my question is:  shall we accept multiple nesting in structure? i.e.
> > 
> > If we don't reject the testcase with an error, then yes.
> 
> Gcc currently accepts the multiple nesting in structure without error.  
> So, we will continue to accept such extension as long as the flex array 
> is at the end of the structure. At the same time, for the case the flex 
> array is in the middle of the structure, issue additional warnings now 
> to discourage such usage, and deprecate this case in a future release.
> 
> Does this sound reasonable? 

Please don't mix several issues - I think the flex array in the
middle of a structure is separate and we shouldn't report that
as flexible_array_type_p or flexible_size_type_p since the size
of the containing structure is not variable.

For diagnostic purposes the intended use case is to treat
a pointer to a structure that appears to have a fixed size
but has (recursive) a member with a flexible array at the end
as having variable size.  Just the same as array_at_struct_end_p
treats this for the case of accesses involving such a type.

For the middle position case that's not the case.

Richard.

> Qing
> > 
> >> struct A {
> >>  int n;
> >>  char data[];/* Content following header */
> >> };
> >> 
> >> struct B {
> >>  int m;
> >>  struct A a;
> >> };
> >> 
> >> struct C {
> >>  int q;
> >>  struct B b;
> >> };
> >> 
> >> Qing
> >>> 
> >>>> thanks.
> >>>> 
> >>>> Qing
> >>>> 
> >>>>> 
> >>>>>> +      return false;
> >>>>>> +    case UNION_TYPE:
> >>>>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
> >>>>>> +	{
> >>>>>> +	  if (TREE_CODE (x) == FIELD_DECL
> >>>>>> +	      && flexible_array_type_p (TREE_TYPE (x)))
> >>>>>> +	    return true;
> >>>>>> +	}
> >>>>>> +      return false;
> >>>>>> +    default:
> >>>>>> +      return false;
> >>>>>> +  }
> >>>>>> +}
> >>>>>> +
> >>>>>> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
> >>>>>>  OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
> >>>>>>  If unknown, return size_unknown (object_size_type).  */
> >>>>>> @@ -633,45 +669,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 the record or union does not have flexible size
> >>>>>> +		       compute the object size directly.  */
> >>>>>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
> >>>>>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
> >>>>>> 		      {
> >>>>>> -			/* compute object size only if v is not a
> >>>>>> -			   flexible array member.  */
> >>>>>> -			if (!is_flexible_array_mem_ref)
> >>>>>> +			if (!flexible_size_type_p (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;
> >>>>>> 
> >>>>> 
> >>>>> -- 
> >>>>> Richard Biener <rguenther@suse.de>
> >>>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> >>>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> >>>>> HRB 36809 (AG Nuernberg)
> >>>> 
> >>>> 
> >>> 
> >>> -- 
> >>> Richard Biener <rguenther@suse.de>
> >>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> >>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> >>> HRB 36809 (AG Nuernberg)
> >> 
> >> 
> > 
> > -- 
> > Richard Biener <rguenther@suse.de>
> > SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> > Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> > HRB 36809 (AG Nuernberg)
> 
>
  
Qing Zhao Feb. 3, 2023, 1:17 p.m. UTC | #10
> On Feb 3, 2023, at 2:49 AM, Richard Biener <rguenther@suse.de> wrote:
> 
> On Thu, 2 Feb 2023, Qing Zhao wrote:
> 
>> 
>> 
>>> On Feb 2, 2023, at 8:54 AM, Richard Biener <rguenther@suse.de> wrote:
>>> 
>>> On Thu, 2 Feb 2023, Qing Zhao wrote:
>>> 
>>>> 
>>>> 
> 
> [...]
> 
>>>>>>>> +	return flexible_size_type_p (TREE_TYPE (last));
>>>>>>> 
>>>>>>> For types with many members this can become quite slow (IIRC we had
>>>>>>> bugs about similar walks of all fields in types), and this function
>>>>>>> looks like it's invoked multiple times on the same type per TU.
>>>>>>> 
>>>>>>> In principle the property is fixed at the time we lay out a record
>>>>>>> type, so we might want to compute it at that time and record the
>>>>>>> result.
>>>>>> 
>>>>>> You mean in FE? 
>>>>> 
>>>>> Yes, either in the frontend or in the middle-ends layout_type.
>>>>> 
>>>>>> Yes, that?s better and cleaner.
>>>>>> 
>>>>>> I will add one more field in the TYPE structure to record this information and check this field during middle end.
>>>>>> 
>>>>>> I had the same thought in the beginning, but not sure whether adding a 
>>>>>> new field in IR is necessary or not, other places in middle end might 
>>>>>> not use this new field.
>>>>> 
>>>>> It might be interesting to search for other code walking all fields of
>>>>> a type to determine this or similar info.
>>>> 
>>>> There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:
>>>> 
>>>> /* Determine whether TYPE is a structure with a flexible array member,
>>>>  or a union containing such a structure (possibly recursively).  */
>>>> flexible_array_type_p
>>>> 
>>>> However, this routine is a little different than the one I tried to add:
>>>> 
>>>> In the current routine ?flexible_array_type_p?,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.
>>>> 
>>>> So, my question is:  shall we accept multiple nesting in structure? i.e.
>>> 
>>> If we don't reject the testcase with an error, then yes.
>> 
>> Gcc currently accepts the multiple nesting in structure without error.  
>> So, we will continue to accept such extension as long as the flex array 
>> is at the end of the structure. At the same time, for the case the flex 
>> array is in the middle of the structure, issue additional warnings now 
>> to discourage such usage, and deprecate this case in a future release.
>> 
>> Does this sound reasonable? 
> 
> Please don't mix several issues - I think the flex array in the
> middle of a structure is separate and we shouldn't report that
> as flexible_array_type_p or flexible_size_type_p since the size
> of the containing structure is not variable.
Agreed on this.

My major question here is (for documentation change, sorry for mixing this thread with the documentation change): do we need to document this case together with the case in which struct with flex array is embedded into another structure? (As a GCC extension?)
> 
> For diagnostic purposes the intended use case is to treat
> a pointer to a structure that appears to have a fixed size
> but has (recursive) a member with a flexible array at the end
> as having variable size.  Just the same as array_at_struct_end_p
> treats this for the case of accesses involving such a type.

Yes. 
> 
> For the middle position case that's not the case.
Yes. 

Thanks.

Qing
> 
> Richard.
> 
>> Qing
>>> 
>>>> struct A {
>>>> int n;
>>>> char data[];/* Content following header */
>>>> };
>>>> 
>>>> struct B {
>>>> int m;
>>>> struct A a;
>>>> };
>>>> 
>>>> struct C {
>>>> int q;
>>>> struct B b;
>>>> };
>>>> 
>>>> Qing
>>>>> 
>>>>>> thanks.
>>>>>> 
>>>>>> Qing
>>>>>> 
>>>>>>> 
>>>>>>>> +      return false;
>>>>>>>> +    case UNION_TYPE:
>>>>>>>> +      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
>>>>>>>> +	{
>>>>>>>> +	  if (TREE_CODE (x) == FIELD_DECL
>>>>>>>> +	      && flexible_array_type_p (TREE_TYPE (x)))
>>>>>>>> +	    return true;
>>>>>>>> +	}
>>>>>>>> +      return false;
>>>>>>>> +    default:
>>>>>>>> +      return false;
>>>>>>>> +  }
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>>>>>>>> OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>>>>>>>> If unknown, return size_unknown (object_size_type).  */
>>>>>>>> @@ -633,45 +669,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 the record or union does not have flexible size
>>>>>>>> +		       compute the object size directly.  */
>>>>>>>> +		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
>>>>>>>> +			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
>>>>>>>> 		      {
>>>>>>>> -			/* compute object size only if v is not a
>>>>>>>> -			   flexible array member.  */
>>>>>>>> -			if (!is_flexible_array_mem_ref)
>>>>>>>> +			if (!flexible_size_type_p (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;
>>>>>>>> 
>>>>>>> 
>>>>>>> -- 
>>>>>>> Richard Biener <rguenther@suse.de>
>>>>>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
>>>>>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
>>>>>>> HRB 36809 (AG Nuernberg)
>>>>>> 
>>>>>> 
>>>>> 
>>>>> -- 
>>>>> Richard Biener <rguenther@suse.de>
>>>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
>>>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
>>>>> HRB 36809 (AG Nuernberg)
>>>> 
>>>> 
>>> 
>>> -- 
>>> Richard Biener <rguenther@suse.de>
>>> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
>>> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
>>> HRB 36809 (AG Nuernberg)
>> 
>> 
> 
> -- 
> Richard Biener <rguenther@suse.de>
> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
> Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
> HRB 36809 (AG Nuernberg)
  
Richard Biener Feb. 6, 2023, 9:31 a.m. UTC | #11
On Fri, 3 Feb 2023, Qing Zhao wrote:

> 
> 
> > On Feb 3, 2023, at 2:49 AM, Richard Biener <rguenther@suse.de> wrote:
> > 
> > On Thu, 2 Feb 2023, Qing Zhao wrote:
> > 
> >> 
> >> 
> >>> On Feb 2, 2023, at 8:54 AM, Richard Biener <rguenther@suse.de> wrote:
> >>> 
> >>> On Thu, 2 Feb 2023, Qing Zhao wrote:
> >>> 
> >>>> 
> >>>> 
> > 
> > [...]
> > 
> >>>>>>>> +	return flexible_size_type_p (TREE_TYPE (last));
> >>>>>>> 
> >>>>>>> For types with many members this can become quite slow (IIRC we had
> >>>>>>> bugs about similar walks of all fields in types), and this function
> >>>>>>> looks like it's invoked multiple times on the same type per TU.
> >>>>>>> 
> >>>>>>> In principle the property is fixed at the time we lay out a record
> >>>>>>> type, so we might want to compute it at that time and record the
> >>>>>>> result.
> >>>>>> 
> >>>>>> You mean in FE? 
> >>>>> 
> >>>>> Yes, either in the frontend or in the middle-ends layout_type.
> >>>>> 
> >>>>>> Yes, that?s better and cleaner.
> >>>>>> 
> >>>>>> I will add one more field in the TYPE structure to record this information and check this field during middle end.
> >>>>>> 
> >>>>>> I had the same thought in the beginning, but not sure whether adding a 
> >>>>>> new field in IR is necessary or not, other places in middle end might 
> >>>>>> not use this new field.
> >>>>> 
> >>>>> It might be interesting to search for other code walking all fields of
> >>>>> a type to determine this or similar info.
> >>>> 
> >>>> There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:
> >>>> 
> >>>> /* Determine whether TYPE is a structure with a flexible array member,
> >>>>  or a union containing such a structure (possibly recursively).  */
> >>>> flexible_array_type_p
> >>>> 
> >>>> However, this routine is a little different than the one I tried to add:
> >>>> 
> >>>> In the current routine ?flexible_array_type_p?,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.
> >>>> 
> >>>> So, my question is:  shall we accept multiple nesting in structure? i.e.
> >>> 
> >>> If we don't reject the testcase with an error, then yes.
> >> 
> >> Gcc currently accepts the multiple nesting in structure without error.  
> >> So, we will continue to accept such extension as long as the flex array 
> >> is at the end of the structure. At the same time, for the case the flex 
> >> array is in the middle of the structure, issue additional warnings now 
> >> to discourage such usage, and deprecate this case in a future release.
> >> 
> >> Does this sound reasonable? 
> > 
> > Please don't mix several issues - I think the flex array in the
> > middle of a structure is separate and we shouldn't report that
> > as flexible_array_type_p or flexible_size_type_p since the size
> > of the containing structure is not variable.
> Agreed on this.
> 
> My major question here is (for documentation change, sorry for mixing 
> this thread with the documentation change): do we need to document this 
> case together with the case in which struct with flex array is embedded 
> into another structure? (As a GCC extension?)

I think this should be Josephs call - documenting this might
encourage people to use such an extension, even if it's a bad
one we want to get rid of.

Maybe the easiest thing is to come up with a patch documenting it
which we can then turn into a deprecation note depending on this
outcome.

Richard.
  
Qing Zhao Feb. 6, 2023, 2:38 p.m. UTC | #12
> On Feb 6, 2023, at 4:31 AM, Richard Biener <rguenther@suse.de> wrote:
> 
> On Fri, 3 Feb 2023, Qing Zhao wrote:
> 
>> 
>> 
>>> On Feb 3, 2023, at 2:49 AM, Richard Biener <rguenther@suse.de> wrote:
>>> 
>>> On Thu, 2 Feb 2023, Qing Zhao wrote:
>>> 
>>>> 
>>>> 
>>>>> On Feb 2, 2023, at 8:54 AM, Richard Biener <rguenther@suse.de> wrote:
>>>>> 
>>>>> On Thu, 2 Feb 2023, Qing Zhao wrote:
>>>>> 
>>>>>> 
>>>>>> 
>>> 
>>> [...]
>>> 
>>>>>>>>>> +	return flexible_size_type_p (TREE_TYPE (last));
>>>>>>>>> 
>>>>>>>>> For types with many members this can become quite slow (IIRC we had
>>>>>>>>> bugs about similar walks of all fields in types), and this function
>>>>>>>>> looks like it's invoked multiple times on the same type per TU.
>>>>>>>>> 
>>>>>>>>> In principle the property is fixed at the time we lay out a record
>>>>>>>>> type, so we might want to compute it at that time and record the
>>>>>>>>> result.
>>>>>>>> 
>>>>>>>> You mean in FE? 
>>>>>>> 
>>>>>>> Yes, either in the frontend or in the middle-ends layout_type.
>>>>>>> 
>>>>>>>> Yes, that?s better and cleaner.
>>>>>>>> 
>>>>>>>> I will add one more field in the TYPE structure to record this information and check this field during middle end.
>>>>>>>> 
>>>>>>>> I had the same thought in the beginning, but not sure whether adding a 
>>>>>>>> new field in IR is necessary or not, other places in middle end might 
>>>>>>>> not use this new field.
>>>>>>> 
>>>>>>> It might be interesting to search for other code walking all fields of
>>>>>>> a type to determine this or similar info.
>>>>>> 
>>>>>> There is one which is defined in tree.cc but only is referenced in c/c-decl.cc:
>>>>>> 
>>>>>> /* Determine whether TYPE is a structure with a flexible array member,
>>>>>> or a union containing such a structure (possibly recursively).  */
>>>>>> flexible_array_type_p
>>>>>> 
>>>>>> However, this routine is a little different than the one I tried to add:
>>>>>> 
>>>>>> In the current routine ?flexible_array_type_p?,  only one level nesting in the structure is accepted, multiple nesting in structure is not permitted.
>>>>>> 
>>>>>> So, my question is:  shall we accept multiple nesting in structure? i.e.
>>>>> 
>>>>> If we don't reject the testcase with an error, then yes.
>>>> 
>>>> Gcc currently accepts the multiple nesting in structure without error.  
>>>> So, we will continue to accept such extension as long as the flex array 
>>>> is at the end of the structure. At the same time, for the case the flex 
>>>> array is in the middle of the structure, issue additional warnings now 
>>>> to discourage such usage, and deprecate this case in a future release.
>>>> 
>>>> Does this sound reasonable? 
>>> 
>>> Please don't mix several issues - I think the flex array in the
>>> middle of a structure is separate and we shouldn't report that
>>> as flexible_array_type_p or flexible_size_type_p since the size
>>> of the containing structure is not variable.
>> Agreed on this.
>> 
>> My major question here is (for documentation change, sorry for mixing 
>> this thread with the documentation change): do we need to document this 
>> case together with the case in which struct with flex array is embedded 
>> into another structure? (As a GCC extension?)
> 
> I think this should be Josephs call - documenting this might
> encourage people to use such an extension, even if it's a bad
> one we want to get rid of.
That’s true...
> 
> Maybe the easiest thing is to come up with a patch documenting it
> which we can then turn into a deprecation note depending on this
> outcome.

In the other thread for the documentation change, I have listed a plan based on the discussion.
 Could you please take a look at it and provide me some comments in that thread? (I just copied my 
plan below for your convenience)

Thanks.

Qing

==================

In GCC13:

1. Add documentation in extend.texi to include all the following 3 cases as GCC extension:

Case 1: The structure with a flexible array member is the last field of another
structure, for example:

struct flex  { int length; char data[]; }
struct out_flex { int m; struct flex flex_data; }

In the above, flex_data.data[] is considered as a flexible array too.

Case 2: The structure with a flexible array member is the field of another union, for example:

struct flex1  { int length1; char data1[]; }
struct flex2  { int length2; char data2[]; }
union out_flex { struct flex1 flex_data1; struct flex2 flex_data2; }

In the above, flex_data1.data1[] or flex_data2.data2[] is considered as flexible arrays too.

Case 3: The structure with a flexible array member is the middle field of another
structure, for example:

struct flex  { int length; char data[]; }
struct out_flex { int m; struct flex flex_data; int n; }

In the above, flex_data.data[] is allowed to be extended flexibly to
the padding. E.g, up to 4 elements.

However, relying on space in struct padding is a bad programming practice,  compilers do not 
handle such extension consistently, and any code relying on this behavior should be modified
to ensure that flexible array members only end up at the ends of structures.

Please use warning option -Wgnu-variable-sized-type-not-at-end (to be consistent with CLANG) 
to identify all such cases in the source code and modify them. This extension will be deprecated
from gcc in the next release.

2. Add a new warning option -Wgnu-varaible-sized-type-not-at-end to warn such usage.

In GCC14:

1. Include this new warning -Wgnu-varaible-sized-type-not-at-end to -Wall
2. Deprecate this extension from GCC. (Or delay this to next release?).


> 
> Richard.
  
Joseph Myers Feb. 6, 2023, 11:14 p.m. UTC | #13
On Mon, 6 Feb 2023, Qing Zhao via Gcc-patches wrote:

> In GCC14:
> 
> 1. Include this new warning -Wgnu-varaible-sized-type-not-at-end to -Wall
> 2. Deprecate this extension from GCC. (Or delay this to next release?).

Any deprecation, or inclusion in -Wall, would best come with evidence 
about the prevalance of use (possibly unintentional, probably undesirable) 
of these extensions.  For example, maybe someone could do a distribution 
rebuild with a patch to enable these warnings and report the results?

Various misuses of flexible array members are only pedwarns-if-pedantic 
because of such uses - and while the original motivating case 
<https://gcc.gnu.org/legacy-ml/gcc-patches/2002-08/msg01149.html> was 
_G_config.h, which has since been fixed (though existing installed headers 
from old glibc would need fixincluding, at least if it becomes an error), 
it's very plausible there are uses of these extensions elsewhere.
  
Qing Zhao Feb. 7, 2023, 2:54 p.m. UTC | #14
Hi, Joseph,


> On Feb 6, 2023, at 6:14 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> 
> On Mon, 6 Feb 2023, Qing Zhao via Gcc-patches wrote:
> 
>> In GCC14:
>> 
>> 1. Include this new warning -Wgnu-varaible-sized-type-not-at-end to -Wall
>> 2. Deprecate this extension from GCC. (Or delay this to next release?).
> 
> Any deprecation, or inclusion in -Wall, would best come with evidence 
> about the prevalance of use (possibly unintentional, probably undesirable) 
> of these extensions.  For example, maybe someone could do a distribution 
> rebuild with a patch to enable these warnings and report the results?
Yes, before we deprecate this extension, it’s better to make sure all such
 misuses are updated already.
> 
> Various misuses of flexible array members are only pedwarns-if-pedantic 
> because of such uses - and while the original motivating case 
> <https://gcc.gnu.org/legacy-ml/gcc-patches/2002-08/msg01149.html>

Just checked this patch (which has been in GCC source tree already), the routine flexible_array_type_p 
+/* Determine whether TYPE is a structure with a flexible array member,
+   or a union containing such a structure (possibly recursively).  */
+
+static bool
+flexible_array_type_p (type)

Did not include the following cases:

1.  Structure with flexible array member embedded into other structures recursively, for example:

struct A {
  int n;
  char data[];
};

struct B {
  int m;
  struct A a;
};

struct C {
  int q;
  struct B b;
};

In the above, “struct C” will not be caught by this routine.

Shall “struct C” be included?

2. Only C99 standard flexible array member be included, [0] and [1] are not included, for example:

struct A {
  int n;
  char data[0];
};

struct B {
  int m;
  struct A a;
};

struct C {
  int q;
  struct B b;
};

In the above, “struct B” and “struct C” will not be caught by this routine.

Shall “the above struct B” and “struct C” be included per -fstrict-flex-arrays?

Could you please take a look at my latest patch:

https://gcc.gnu.org/pipermail/gcc-patches/2023-February/611445.html

To see whether the new bit “TYPE_INCLUDE_FLEXARRAY” covers the above “flexible_array_type_p”? Then we can merge them together?


> was 
> _G_config.h, which has since been fixed (though existing installed headers 
> from old glibc would need fixincluding, at least if it becomes an error), 
> it's very plausible there are uses of these extensions elsewhere.
If this is the case, we should definitely deprecate this extension.

Thanks.

Qing
> 
> -- 
> Joseph S. Myers
> joseph@codesourcery.com
  
Siddhesh Poyarekar Feb. 7, 2023, 3:28 p.m. UTC | #15
On 2023-02-06 18:14, Joseph Myers wrote:
> On Mon, 6 Feb 2023, Qing Zhao via Gcc-patches wrote:
> 
>> In GCC14:
>>
>> 1. Include this new warning -Wgnu-varaible-sized-type-not-at-end to -Wall
>> 2. Deprecate this extension from GCC. (Or delay this to next release?).
> 
> Any deprecation, or inclusion in -Wall, would best come with evidence
> about the prevalance of use (possibly unintentional, probably undesirable)
> of these extensions.  For example, maybe someone could do a distribution
> rebuild with a patch to enable these warnings and report the results?

FWIW, Fred from our team has been working on a mass prebuilder that can 
be used for this kind of distribution-wide validation.  I've used it for 
_FORTIFY_SOURCE validation as well as coverage analysis.

I can help you with this Qing, once you have a patch ready.

Sid

[1] https://gitlab.com/fedora/packager-tools/mass-prebuild/
  
Qing Zhao Feb. 7, 2023, 3:38 p.m. UTC | #16
> On Feb 7, 2023, at 10:28 AM, Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
> 
> On 2023-02-06 18:14, Joseph Myers wrote:
>> On Mon, 6 Feb 2023, Qing Zhao via Gcc-patches wrote:
>>> In GCC14:
>>> 
>>> 1. Include this new warning -Wgnu-varaible-sized-type-not-at-end to -Wall
>>> 2. Deprecate this extension from GCC. (Or delay this to next release?).
>> Any deprecation, or inclusion in -Wall, would best come with evidence
>> about the prevalance of use (possibly unintentional, probably undesirable)
>> of these extensions.  For example, maybe someone could do a distribution
>> rebuild with a patch to enable these warnings and report the results?
> 
> FWIW, Fred from our team has been working on a mass prebuilder that can be used for this kind of distribution-wide validation.  I've used it for _FORTIFY_SOURCE validation as well as coverage analysis.
> 
> I can help you with this Qing, once you have a patch ready.

Thanks a lot!

Qing
> 
> Sid
> 
> [1] https://gitlab.com/fedora/packager-tools/mass-prebuild/
  
Joseph Myers Feb. 7, 2023, 7:17 p.m. UTC | #17
On Tue, 7 Feb 2023, Qing Zhao via Gcc-patches wrote:

> 1.  Structure with flexible array member embedded into other structures 
> recursively, for example:
> 
> struct A {
>   int n;
>   char data[];
> };
> 
> struct B {
>   int m;
>   struct A a;
> };
> 
> struct C {
>   int q;
>   struct B b;
> };
> 
> In the above, “struct C” will not be caught by this routine.

Because struct B is diagnosed with -pedantic when it embed struct A, there 
is no need for -pedantic to diagnose struct C as well when it embeds 
struct B.

> 2. Only C99 standard flexible array member be included, [0] and [1] are 
> not included, for example:

Obviously we can't diagnose use of structures with [1] trailing members, 
because it's perfectly valid to embed those structures at any position 
inside other structures.  And the same is the case for the [0] extension 
when it's used to mean "empty array" rather than "flexible array".

Note that my comments above are about what diagnostics are appropriate 
under the standard.  They are *not* about how code generation might allow 
for possible uses of certain source code constructs as if they were 
flexible array members.  The two contexts may very well require different 
notions of what counts as a flexible array member.
  
Qing Zhao Feb. 7, 2023, 7:57 p.m. UTC | #18
> On Feb 7, 2023, at 2:17 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> 
> On Tue, 7 Feb 2023, Qing Zhao via Gcc-patches wrote:
> 
>> 1.  Structure with flexible array member embedded into other structures 
>> recursively, for example:
>> 
>> struct A {
>>  int n;
>>  char data[];
>> };
>> 
>> struct B {
>>  int m;
>>  struct A a;
>> };
>> 
>> struct C {
>>  int q;
>>  struct B b;
>> };
>> 
>> In the above, “struct C” will not be caught by this routine.
> 
> Because struct B is diagnosed with -pedantic when it embed struct A, there 
> is no need for -pedantic to diagnose struct C as well when it embeds 
> struct B.

Oh, yes.
Then, this routine (flexible_array_type_p) is mainly for diagnostic purpose.
It cannot be used to determine whether the structure/union type recursively
include a flexible array member at the end.

Is my understanding correct?

> 
>> 2. Only C99 standard flexible array member be included, [0] and [1] are 
>> not included, for example:
> 
> Obviously we can't diagnose use of structures with [1] trailing members, 
> because it's perfectly valid to embed those structures at any position 
> inside other structures.  And the same is the case for the [0] extension 
> when it's used to mean "empty array" rather than "flexible array".

With the -fstrict-flex-arrays available, we should be able to diagnose
the flexible array member per gnu extension (i.e [0] or [1]) the same as []. 

> 
> Note that my comments above are about what diagnostics are appropriate 
> under the standard.  They are *not* about how code generation might allow 
> for possible uses of certain source code constructs as if they were 
> flexible array members.  The two contexts may very well require different 
> notions of what counts as a flexible array member.

Yes. That’s right.

Thanks.

Qing
> 
> -- 
> Joseph S. Myers
> joseph@codesourcery.com
  
Joseph Myers Feb. 7, 2023, 11:37 p.m. UTC | #19
On Tue, 7 Feb 2023, Qing Zhao via Gcc-patches wrote:

> Then, this routine (flexible_array_type_p) is mainly for diagnostic purpose.
> It cannot be used to determine whether the structure/union type recursively
> include a flexible array member at the end.
> 
> Is my understanding correct?

My comments were about basic principles of what gets diagnosed, and the 
need for different predicates in different contexts; I wasn't trying to 
assert anything about how that maps onto what functions should be used in 
what contexts.

> >> 2. Only C99 standard flexible array member be included, [0] and [1] are 
> >> not included, for example:
> > 
> > Obviously we can't diagnose use of structures with [1] trailing members, 
> > because it's perfectly valid to embed those structures at any position 
> > inside other structures.  And the same is the case for the [0] extension 
> > when it's used to mean "empty array" rather than "flexible array".
> 
> With the -fstrict-flex-arrays available, we should be able to diagnose
> the flexible array member per gnu extension (i.e [0] or [1]) the same as []. 

There are different sorts of diagnostic that might be involved.

* Simply having [0] or [1] at the end of a structure embedded in another 
structure isn't appropriate to diagnose, because [0] and [1] have 
perfectly good meanings in such a context that aren't trying to be 
flexible array members at all.  [0] might be an empty type (possibly one 
that wouldn't be empty when built with a different configuration).  [1] 
might be the use of arrays in C to produce a passed-by-reference type.

* Trying to use such an embedded [0] or [1] array as if it were a flexible 
array member - i.e. accessing any member of the [0] array, or any member 
other than the [0] member of the [1] array - *is* a sign of the 
problematic use as a flexible array member, that might be appropriate to 
diagnose.  (Actually I'd guess the array index tends to be non-constant in 
accesses, and it would be odd to use a non-constant index when you mean 
that constant always to be 0, which it would need to be in the 
non-flexible case.)
  
Qing Zhao Feb. 8, 2023, 3:06 p.m. UTC | #20
> On Feb 7, 2023, at 6:37 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> 
> On Tue, 7 Feb 2023, Qing Zhao via Gcc-patches wrote:
> 
>> Then, this routine (flexible_array_type_p) is mainly for diagnostic purpose.
>> It cannot be used to determine whether the structure/union type recursively
>> include a flexible array member at the end.
>> 
>> Is my understanding correct?
> 
> My comments were about basic principles of what gets diagnosed, and the 
> need for different predicates in different contexts; I wasn't trying to 
> assert anything about how that maps onto what functions should be used in 
> what contexts.
Okay. 

But I noticed that “flexible_array_type_p” later was moved from FE to
 middle-end and put into tree.cc, tree.h as a general utility routine, and to 

/* Determine whether TYPE is a structure with a flexible array member,
   or a union containing such a structure (possibly recursively).  */

However, since this routine does not cover the cases when the structure 
with flexible array member was recursively embedded into structures, (which we 
agreed that it should be considered as a flexible sized type). 

Therefore, I feel that It might not be proper to include this routine in middle end 
(and actually no other places In middle end use this routine so far).

That’s the reason I asked the previous question. 

It might be better to move the routine “flexible_array_type_p” back from middle-end to
FE for the diagnosis purpose only. 


> 
>>>> 2. Only C99 standard flexible array member be included, [0] and [1] are 
>>>> not included, for example:
>>> 
>>> Obviously we can't diagnose use of structures with [1] trailing members, 
>>> because it's perfectly valid to embed those structures at any position 
>>> inside other structures.  And the same is the case for the [0] extension 
>>> when it's used to mean "empty array" rather than "flexible array".
>> 
>> With the -fstrict-flex-arrays available, we should be able to diagnose
>> the flexible array member per gnu extension (i.e [0] or [1]) the same as []. 
> 
> There are different sorts of diagnostic that might be involved.
> 
> * Simply having [0] or [1] at the end of a structure embedded in another 
> structure isn't appropriate to diagnose, because [0] and [1] have 
> perfectly good meanings in such a context that aren't trying to be 
> flexible array members at all.  [0] might be an empty type (possibly one 
> that wouldn't be empty when built with a different configuration).  [1] 
> might be the use of arrays in C to produce a passed-by-reference type.


So, you mean, by default, Only having [] at the end of a structure embedded
 in another structure is considered to be flexible sized type?

i.e.
struct flex { int n; int data[ ]; };
struct out_flex_end { int m; struct flex0 flex_data; }; 
struct outer_flex_end{ int p; struct out_flex_end0 out_flex_data; }; 

In the above, all “flex”, “out_flex_end” and “outer_flex_end” are flexible sized type.

But:

struct flex0 { int n; int data[0]; };
struct out_flex_end0 { int m; struct flex0 flex_data; }; 
struct outer_flex_end0 { int p; struct out_flex_end0 out_flex_data; }; 

In the above, only “flex0” is flexible sized type by default. 
But “out_flex_end0” and “out_flex_end0” are Not considered as flexible sized type by default? 

> 
> * Trying to use such an embedded [0] or [1] array as if it were a flexible 
> array member - i.e. accessing any member of the [0] array, or any member 
> other than the [0] member of the [1] array - *is* a sign of the 
> problematic use as a flexible array member, that might be appropriate to 
> diagnose.

Yes, this was diagnosed with -Wstrict-flex-arrays + -fstrict-flex-arrays=n.

thanks.

Qing

>  (Actually I'd guess the array index tends to be non-constant in 
> accesses, and it would be odd to use a non-constant index when you mean 
> that constant always to be 0, which it would need to be in the 
> non-flexible case.)
> 
> -- 
> Joseph S. Myers
> joseph@codesourcery.com
  
Joseph Myers Feb. 8, 2023, 7:09 p.m. UTC | #21
On Wed, 8 Feb 2023, Qing Zhao via Gcc-patches wrote:

> But I noticed that “flexible_array_type_p” later was moved from FE to
>  middle-end and put into tree.cc, tree.h as a general utility routine, and to 
> 
> /* Determine whether TYPE is a structure with a flexible array member,
>    or a union containing such a structure (possibly recursively).  */
> 
> However, since this routine does not cover the cases when the structure 
> with flexible array member was recursively embedded into structures, (which we 
> agreed that it should be considered as a flexible sized type). 
> 
> Therefore, I feel that It might not be proper to include this routine in middle end 
> (and actually no other places In middle end use this routine so far).

I think we've established that diagnostics and at least some middle-end 
purposes need different conditions.  Diagnostics for nesting a structure 
with a flexible array member inside another structure should only count [] 
as a flexible array member, whereas permitting flexible array uses in the 
middle end should allow [0] and [1] under some circumstances (depending on 
command-line options).

> But:
> 
> struct flex0 { int n; int data[0]; };
> struct out_flex_end0 { int m; struct flex0 flex_data; }; 
> struct outer_flex_end0 { int p; struct out_flex_end0 out_flex_data; }; 
> 
> In the above, only “flex0” is flexible sized type by default. 
> But “out_flex_end0” and “out_flex_end0” are Not considered as flexible sized type by default? 

It would be OK (and I'm not saying here that this is necessarily 
desirable), since that's at the end of another structure rather than in 
the middle, to consider them flexible for the purposes of code generation.

What must be avoided is -pedantic diagnostics for

struct flex1 { int n; int data[1]; };
struct out_flex_end1 { int m; struct flex1 flex_data; };

regardless of whether considered flexible or not, since that's clearly 
valid in standard C.
  
Siddhesh Poyarekar Feb. 8, 2023, 7:20 p.m. UTC | #22
On 2023-02-08 14:09, Joseph Myers wrote:
> What must be avoided is -pedantic diagnostics for
> 
> struct flex1 { int n; int data[1]; };
> struct out_flex_end1 { int m; struct flex1 flex_data; };
> 
> regardless of whether considered flexible or not, since that's clearly
> valid in standard C.
> 

Are you sure about "regardless of whether considered flexible or not", 
since ISTM the validity of the above in standard C is limited to when 
it's considered a non-flexible array.  So with -pedantic it shouldn't 
warn, but it also then shouldn't consider it a flexible array.

In other words, perhaps it makes sense to imply -fstrict-flex-arrays 
with -pedantic?

Sid
  
Joseph Myers Feb. 8, 2023, 8:51 p.m. UTC | #23
On Wed, 8 Feb 2023, Siddhesh Poyarekar wrote:

> On 2023-02-08 14:09, Joseph Myers wrote:
> > What must be avoided is -pedantic diagnostics for
> > 
> > struct flex1 { int n; int data[1]; };
> > struct out_flex_end1 { int m; struct flex1 flex_data; };
> > 
> > regardless of whether considered flexible or not, since that's clearly
> > valid in standard C.
> > 
> 
> Are you sure about "regardless of whether considered flexible or not", since
> ISTM the validity of the above in standard C is limited to when it's
> considered a non-flexible array.  So with -pedantic it shouldn't warn, but it
> also then shouldn't consider it a flexible array.
> 
> In other words, perhaps it makes sense to imply -fstrict-flex-arrays with
> -pedantic?

There should be no code generation effects from -pedantic.
  
Qing Zhao Feb. 8, 2023, 10:53 p.m. UTC | #24
> On Feb 8, 2023, at 2:20 PM, Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
> 
> On 2023-02-08 14:09, Joseph Myers wrote:
>> What must be avoided is -pedantic diagnostics for
>> struct flex1 { int n; int data[1]; };
>> struct out_flex_end1 { int m; struct flex1 flex_data; };
>> regardless of whether considered flexible or not, since that's clearly
>> valid in standard C.
> 
> Are you sure about "regardless of whether considered flexible or not", since ISTM the validity of the above in standard C is limited to when it's considered a non-flexible array.  So with -pedantic it shouldn't warn, but it also then shouldn't consider it a flexible array.
> 
> In other words, perhaps it makes sense to imply -fstrict-flex-arrays with -pedantic?
I think -pedantic might imply -fstrict-flex-arrays=3, but not -fstrict-flex-arrays=n when n < 3. Right?

Qing
> 
> Sid
  
Qing Zhao Feb. 8, 2023, 11:18 p.m. UTC | #25
> On Feb 8, 2023, at 2:09 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> 
> On Wed, 8 Feb 2023, Qing Zhao via Gcc-patches wrote:
> 
>> But I noticed that “flexible_array_type_p” later was moved from FE to
>> middle-end and put into tree.cc, tree.h as a general utility routine, and to 
>> 
>> /* Determine whether TYPE is a structure with a flexible array member,
>>   or a union containing such a structure (possibly recursively).  */
>> 
>> However, since this routine does not cover the cases when the structure 
>> with flexible array member was recursively embedded into structures, (which we 
>> agreed that it should be considered as a flexible sized type). 
>> 
>> Therefore, I feel that It might not be proper to include this routine in middle end 
>> (and actually no other places In middle end use this routine so far).
> 
> I think we've established that diagnostics and at least some middle-end 
> purposes need different conditions.

Yes, agreed.

>  Diagnostics for nesting a structure 
> with a flexible array member inside another structure should only count [] 
> as a flexible array member,

Diagnostics should be consistent with the documentation. 
The use cases that violate what is defined in documentation should be diagnostics. 

This include both C standard and GCC extension. 
For C standard violation, -pedantic will report them.
For GCC extension, the corresponding warning message should report them.

And both such warning can be issued either in FE or in Middle end. 

Is the above understand correct?

> whereas permitting flexible array uses in the 
> middle end should allow [0] and [1] under some circumstances (depending on 
> command-line options).

> 
>> But:
>> 
>> struct flex0 { int n; int data[0]; };
>> struct out_flex_end0 { int m; struct flex0 flex_data; }; 
>> struct outer_flex_end0 { int p; struct out_flex_end0 out_flex_data; }; 
>> 
>> In the above, only “flex0” is flexible sized type by default. 
>> But “out_flex_end0” and “out_flex_end0” are Not considered as flexible sized type by default? 
> 
> It would be OK (and I'm not saying here that this is necessarily 
> desirable), since that's at the end of another structure rather than in 
> the middle, to consider them flexible for the purposes of code generation.

This is the part I am still not very clear and not feel very comfortable:

In the documentation on GCC extension of embedding structure with flexible array member into another structure/union, 

Should we include [0], [1] and [n] cases as GCC extension by default? Or we only include [] for nested struct as an extension?

For example:

struct flex0  { int length; char data[0]; };

struct out_flex0 { int m; struct flex0 flex_data; };
struct out_flex0_mid  {  struct flex0 flex_data; int m};

Should we treat the above out_flex0->flex_data as flexible array by default?
Should we issue warning for the above out_flex9_mid with a new warning option -Wgnu-variable-sized-type-at-end?

How about the following:
struct flex1  { int length; char data[1]; };

struct out_flex1 { int m; struct flex1 flex_data; };
struct out_flex1_mid  {  struct flex1 flex_data; int m};

And:
struct flexn { int length; char data[4]; };

struct out_flexn { int m; struct flexn flex_data; };
struct out_flexn_mid  {  struct flexn flex_data; int m};

????


> 
> What must be avoided is -pedantic diagnostics for
> 
> struct flex1 { int n; int data[1]; };
> struct out_flex_end1 { int m; struct flex1 flex_data; };
> 
> regardless of whether considered flexible or not, since that's clearly 
> valid in standard C.
Yes, agreed.

Thanks.

Qing
> 
> -- 
> Joseph S. Myers
> joseph@codesourcery.com
  
Richard Biener Feb. 9, 2023, 10:35 a.m. UTC | #26
On Wed, 8 Feb 2023, Qing Zhao wrote:

> 
> 
> > On Feb 7, 2023, at 6:37 PM, Joseph Myers <joseph@codesourcery.com> wrote:
> > 
> > On Tue, 7 Feb 2023, Qing Zhao via Gcc-patches wrote:
> > 
> >> Then, this routine (flexible_array_type_p) is mainly for diagnostic purpose.
> >> It cannot be used to determine whether the structure/union type recursively
> >> include a flexible array member at the end.
> >> 
> >> Is my understanding correct?
> > 
> > My comments were about basic principles of what gets diagnosed, and the 
> > need for different predicates in different contexts; I wasn't trying to 
> > assert anything about how that maps onto what functions should be used in 
> > what contexts.
> Okay. 
> 
> But I noticed that “flexible_array_type_p” later was moved from FE to
>  middle-end and put into tree.cc, tree.h as a general utility routine, and to 
> 
> /* Determine whether TYPE is a structure with a flexible array member,
>    or a union containing such a structure (possibly recursively).  */
> 
> However, since this routine does not cover the cases when the structure 
> with flexible array member was recursively embedded into structures, (which we 
> agreed that it should be considered as a flexible sized type). 
> 
> Therefore, I feel that It might not be proper to include this routine in middle end 
> (and actually no other places In middle end use this routine so far).
> 
> That’s the reason I asked the previous question. 
> 
> It might be better to move the routine “flexible_array_type_p” back from middle-end to
> FE for the diagnosis purpose only. 

It's always dangerous to move functions with such a descriptive name to
a place where it suggests wider use is applicable.  Also since
objc/objc-act.cc has a function with the same name (if that had same
content before r10-5097-g4569f8b3652ae1 then the function should
have been moved to c-family/ instead).

The only "middle-end" use, btw., is in ./config/nios2/nios2.cc,
intoduced by said revision and your points probably mean this change
was misguided and flexible_array_type_p isn't the thing to fix here.
flexible-size _objects_ are clearly denoted by DECL_SIZE being
non-constant - though the case of .sdata is quite odd and the issue
fixed is probably running into a bug elsewhere ...

Sandra?

Thanks,
Richard.
  
Qing Zhao Feb. 9, 2023, 1:44 p.m. UTC | #27
> On Feb 9, 2023, at 5:35 AM, Richard Biener <rguenther@suse.de> wrote:
> 
> On Wed, 8 Feb 2023, Qing Zhao wrote:
> 
>> 
>> 
>>> On Feb 7, 2023, at 6:37 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>>> 
>>> On Tue, 7 Feb 2023, Qing Zhao via Gcc-patches wrote:
>>> 
>>>> Then, this routine (flexible_array_type_p) is mainly for diagnostic purpose.
>>>> It cannot be used to determine whether the structure/union type recursively
>>>> include a flexible array member at the end.
>>>> 
>>>> Is my understanding correct?
>>> 
>>> My comments were about basic principles of what gets diagnosed, and the 
>>> need for different predicates in different contexts; I wasn't trying to 
>>> assert anything about how that maps onto what functions should be used in 
>>> what contexts.
>> Okay. 
>> 
>> But I noticed that “flexible_array_type_p” later was moved from FE to
>> middle-end and put into tree.cc, tree.h as a general utility routine, and to 
>> 
>> /* Determine whether TYPE is a structure with a flexible array member,
>>   or a union containing such a structure (possibly recursively).  */
>> 
>> However, since this routine does not cover the cases when the structure 
>> with flexible array member was recursively embedded into structures, (which we 
>> agreed that it should be considered as a flexible sized type). 
>> 
>> Therefore, I feel that It might not be proper to include this routine in middle end 
>> (and actually no other places In middle end use this routine so far).
>> 
>> That’s the reason I asked the previous question. 
>> 
>> It might be better to move the routine “flexible_array_type_p” back from middle-end to
>> FE for the diagnosis purpose only. 
> 
> It's always dangerous to move functions with such a descriptive name to
> a place where it suggests wider use is applicable.  Also since
> objc/objc-act.cc has a function with the same name (if that had same
> content before r10-5097-g4569f8b3652ae1 then the function should
> have been moved to c-family/ instead).
The routine “flexible_array_type_p” in objc/objc-act.cc is a little different from the one in middle-end:

It includes the ARRAY_TYPE in addition to RECORD_TYPE and UNION_TYPE.
> 
> The only "middle-end" use, btw., is in ./config/nios2/nios2.cc,
> intoduced by said revision and your points probably mean this change
> was misguided and flexible_array_type_p isn't the thing to fix here.

Yes, I guess so. 

Qing
> flexible-size _objects_ are clearly denoted by DECL_SIZE being
> non-constant - though the case of .sdata is quite odd and the issue
> fixed is probably running into a bug elsewhere ...
> 
> Sandra?
> 
> Thanks,
> Richard.
  
Qing Zhao Feb. 9, 2023, 2:40 p.m. UTC | #28
> On Feb 8, 2023, at 6:18 PM, Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> 
> 
> 
>> On Feb 8, 2023, at 2:09 PM, Joseph Myers <joseph@codesourcery.com> wrote:
>> 
>> On Wed, 8 Feb 2023, Qing Zhao via Gcc-patches wrote:
>> 
>>> But I noticed that “flexible_array_type_p” later was moved from FE to
>>> middle-end and put into tree.cc, tree.h as a general utility routine, and to 
>>> 
>>> /* Determine whether TYPE is a structure with a flexible array member,
>>>  or a union containing such a structure (possibly recursively).  */
>>> 
>>> However, since this routine does not cover the cases when the structure 
>>> with flexible array member was recursively embedded into structures, (which we 
>>> agreed that it should be considered as a flexible sized type). 
>>> 
>>> Therefore, I feel that It might not be proper to include this routine in middle end 
>>> (and actually no other places In middle end use this routine so far).
>> 
>> I think we've established that diagnostics and at least some middle-end 
>> purposes need different conditions.
> 
> Yes, agreed.
> 
>> Diagnostics for nesting a structure 
>> with a flexible array member inside another structure should only count [] 
>> as a flexible array member,
> 
> Diagnostics should be consistent with the documentation. 
> The use cases that violate what is defined in documentation should be diagnostics. 
> 
> This include both C standard and GCC extension. 
> For C standard violation, -pedantic will report them.
> For GCC extension, the corresponding warning message should report them.
> 
> And both such warning can be issued either in FE or in Middle end. 
> 
> Is the above understand correct?
> 
>> whereas permitting flexible array uses in the 
>> middle end should allow [0] and [1] under some circumstances (depending on 
>> command-line options).
> 
>> 
>>> But:
>>> 
>>> struct flex0 { int n; int data[0]; };
>>> struct out_flex_end0 { int m; struct flex0 flex_data; }; 
>>> struct outer_flex_end0 { int p; struct out_flex_end0 out_flex_data; }; 
>>> 
>>> In the above, only “flex0” is flexible sized type by default. 
>>> But “out_flex_end0” and “out_flex_end0” are Not considered as flexible sized type by default? 
>> 
>> It would be OK (and I'm not saying here that this is necessarily 
>> desirable), since that's at the end of another structure rather than in 
>> the middle, to consider them flexible for the purposes of code generation.
> 
> This is the part I am still not very clear and not feel very comfortable:
> 
> In the documentation on GCC extension of embedding structure with flexible array member into another structure/union, 
> 
> Should we include [0], [1] and [n] cases as GCC extension by default? Or we only include [] for nested struct as an extension?
> 
> For example:
> 
> struct flex0  { int length; char data[0]; };
> 
> struct out_flex0 { int m; struct flex0 flex_data; };
> struct out_flex0_mid  {  struct flex0 flex_data; int m};
> 
> Should we treat the above out_flex0->flex_data as flexible array by default?
> Should we issue warning for the above out_flex9_mid with a new warning option -Wgnu-variable-sized-type-at-end?
> 
> How about the following:
> struct flex1  { int length; char data[1]; };
> 
> struct out_flex1 { int m; struct flex1 flex_data; };
> struct out_flex1_mid  {  struct flex1 flex_data; int m};
> 
> And:
> struct flexn { int length; char data[4]; };
> 
> struct out_flexn { int m; struct flexn flex_data; };
> struct out_flexn_mid  {  struct flexn flex_data; int m};
> 
> ????
> 

More thought on the above:

I think that we need to be more careful with the documentation of the GCC extension on embedding structure with flexible array member into the end of another structure/union. 
As Richard mentioned before, documentation might encourage people to use it…

So, the major question here is:

 in addition to the C99 standard flexible array member [ ], shall we include [0], [1] or even [4] into this extension, and treat the structure with a trailing [0], [1], or [4] embedded into another structure/union still as flexible-sized?

I think that we might need to limit this extension ONLY to C99 standard FAM [ ].  All other [0], [1], or [4] should be excluded from this extension. The reasons are:

1. The real usages of such GCC extension (embedding structure with FAM into another structure/union), as my understanding, the old glibc’s <_G_config.h> (https://gcc.gnu.org/legacy-ml/gcc-patches/2002-08/msg01149.html), and the bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101832, ONLY involved C99 standard FAM;

2. Embedding a structure with C99 FAM [] into the end of another structure, and still treat it flexible sized might have more usages, and as discussed with Kees, it might be reasonable to promote this into a  C standard later if needed.

So, based on this consideration, I think I should only document the following as GCC extension:

struct flex  { int length; char data[ ]; };
struct out_flex { int m; struct flex flex_data; };

Issue warnings for the following: (when the structure is not at the end)

struct out_flex_mid  {  struct flex flex_data; int m};


However, for the trailing [0], [1], or [4], when such structure embedded into the end of another structure, We should NOT treat the outer structure as flexible sized. 
Logically, we will NOT issue warnings when such structure is not at the end. 

Let me know if you have any comment or suggestions.

thanks.

Qing 
> 
>> 
>> What must be avoided is -pedantic diagnostics for
>> 
>> struct flex1 { int n; int data[1]; };
>> struct out_flex_end1 { int m; struct flex1 flex_data; };
>> 
>> regardless of whether considered flexible or not, since that's clearly 
>> valid in standard C.
> Yes, agreed.
> 
> Thanks.
> 
> Qing
>> 
>> -- 
>> Joseph S. Myers
>> joseph@codesourcery.com
  
Kees Cook Feb. 9, 2023, 4:46 p.m. UTC | #29
On Thu, Feb 09, 2023 at 02:40:57PM +0000, Qing Zhao wrote:
> So, the major question here is:
> 
>  in addition to the C99 standard flexible array member [ ], shall we include [0], [1] or even [4] into this extension, and treat the structure with a trailing [0], [1], or [4] embedded into another structure/union still as flexible-sized?
> 
> I think that we might need to limit this extension ONLY to C99 standard FAM [ ].  All other [0], [1], or [4] should be excluded from this extension. The reasons are:
> 
> 1. The real usages of such GCC extension (embedding structure with FAM into another structure/union), as my understanding, the old glibc’s <_G_config.h> (https://gcc.gnu.org/legacy-ml/gcc-patches/2002-08/msg01149.html), and the bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101832, ONLY involved C99 standard FAM;
> 
> 2. Embedding a structure with C99 FAM [] into the end of another structure, and still treat it flexible sized might have more usages, and as discussed with Kees, it might be reasonable to promote this into a  C standard later if needed.
> 
> So, based on this consideration, I think I should only document the following as GCC extension:
> 
> struct flex  { int length; char data[ ]; };
> struct out_flex { int m; struct flex flex_data; };
> 
> Issue warnings for the following: (when the structure is not at the end)
> 
> struct out_flex_mid  {  struct flex flex_data; int m};
> 
> 
> However, for the trailing [0], [1], or [4], when such structure embedded into the end of another structure, We should NOT treat the outer structure as flexible sized. 
> Logically, we will NOT issue warnings when such structure is not at the end. 
> 
> Let me know if you have any comment or suggestions.

FWIW this all sounds correct to me.
  
Qing Zhao Feb. 10, 2023, 3:25 p.m. UTC | #30
Thanks, Kees.

If there is no objection, I will update my patches with this. And send the updated patches soon.

Qing

> On Feb 9, 2023, at 11:46 AM, Kees Cook <keescook@chromium.org> wrote:
> 
> On Thu, Feb 09, 2023 at 02:40:57PM +0000, Qing Zhao wrote:
>> So, the major question here is:
>> 
>> in addition to the C99 standard flexible array member [ ], shall we include [0], [1] or even [4] into this extension, and treat the structure with a trailing [0], [1], or [4] embedded into another structure/union still as flexible-sized?
>> 
>> I think that we might need to limit this extension ONLY to C99 standard FAM [ ].  All other [0], [1], or [4] should be excluded from this extension. The reasons are:
>> 
>> 1. The real usages of such GCC extension (embedding structure with FAM into another structure/union), as my understanding, the old glibc’s <_G_config.h> (https://gcc.gnu.org/legacy-ml/gcc-patches/2002-08/msg01149.html), and the bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101832, ONLY involved C99 standard FAM;
>> 
>> 2. Embedding a structure with C99 FAM [] into the end of another structure, and still treat it flexible sized might have more usages, and as discussed with Kees, it might be reasonable to promote this into a  C standard later if needed.
>> 
>> So, based on this consideration, I think I should only document the following as GCC extension:
>> 
>> struct flex  { int length; char data[ ]; };
>> struct out_flex { int m; struct flex flex_data; };
>> 
>> Issue warnings for the following: (when the structure is not at the end)
>> 
>> struct out_flex_mid  {  struct flex flex_data; int m};
>> 
>> 
>> However, for the trailing [0], [1], or [4], when such structure embedded into the end of another structure, We should NOT treat the outer structure as flexible sized. 
>> Logically, we will NOT issue warnings when such structure is not at the end. 
>> 
>> Let me know if you have any comment or suggestions.
> 
> FWIW this all sounds correct to me.
> 
> -- 
> Kees Cook
  

Patch

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..f38babc5415
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c
@@ -0,0 +1,135 @@ 
+/* 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 <stdio.h>
+
+unsigned n_fails = 0;
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    printf("ok:  %s == %zd\n", #p, p); \
+  else {\
+    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    n_fails++; \
+  } \
+} 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), -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));
+
+    if (n_fails > 0)
+      __builtin_abort ();
+
+    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..aaae99b8d67
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c
@@ -0,0 +1,135 @@ 
+/* 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 <stdio.h>
+
+unsigned n_fails = 0;
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    printf("ok:  %s == %zd\n", #p, p); \
+  else {\
+    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    n_fails++; \
+  } \
+} 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));
+
+    if (n_fails > 0)
+      __builtin_abort ();
+
+    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..424264e2acd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c
@@ -0,0 +1,135 @@ 
+/* 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 <stdio.h>
+
+unsigned n_fails = 0;
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    printf("ok:  %s == %zd\n", #p, p); \
+  else {\
+    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    n_fails++; \
+  } \
+} 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), 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));
+
+    if (n_fails > 0)
+      __builtin_abort ();
+
+    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..8ed6980edf0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c
@@ -0,0 +1,119 @@ 
+/* 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 <stdio.h>
+
+unsigned n_fails = 0;
+
+#define expect(p, _v) do { \
+  size_t v = _v; \
+  if (p == v) \
+    printf("ok:  %s == %zd\n", #p, p); \
+  else {\
+    printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \
+    n_fails++; \
+  } \
+} 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 = (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);
+
+    if (n_fails > 0)
+      __builtin_abort ();
+
+    return 0;
+}
diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
index 9a936a91983..56b78ca2a8c 100644
--- a/gcc/tree-object-size.cc
+++ b/gcc/tree-object-size.cc
@@ -500,6 +500,42 @@  decl_init_size (tree decl, bool min)
   return size;
 }
 
+/* Determine whether TYPE is a structure with a flexible array member
+   per -fstrict-flex-array or a union containing such a structure
+   (possibly recursively).  */
+static bool
+flexible_size_type_p (const_tree type)
+{
+  tree x = NULL_TREE;
+  tree last = NULL_TREE;
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
+	if (TREE_CODE (x) == FIELD_DECL)
+	  last = x;
+      if (last == NULL_TREE)
+	return false;
+      if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE
+	  && !DECL_NOT_FLEXARRAY (last))
+	return true;
+      else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE
+	       || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE)
+	return flexible_size_type_p (TREE_TYPE (last));
+      return false;
+    case UNION_TYPE:
+      for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x))
+	{
+	  if (TREE_CODE (x) == FIELD_DECL
+	      && flexible_array_type_p (TREE_TYPE (x)))
+	    return true;
+	}
+      return false;
+    default:
+      return false;
+  }
+}
+
 /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
    If unknown, return size_unknown (object_size_type).  */
@@ -633,45 +669,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 the record or union does not have flexible size
+		       compute the object size directly.  */
+		    if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE
+			|| TREE_CODE (TREE_TYPE (v)) == UNION_TYPE)
 		      {
-			/* compute object size only if v is not a
-			   flexible array member.  */
-			if (!is_flexible_array_mem_ref)
+			if (!flexible_size_type_p (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;