}
}
+
+/* We set C_TYPE_VARIABLY_MODIFIED for derived types. We will not update
+ array types, pointers to array types, function types and other derived
+ types created while the type was still incomplete. We need to update
+ at least all types for which TYPE_CANONICAL will bet set, because for
+ those we later assume (in c_variably_modified_p) that the bit is
+ up-to-date. */
+
+static void
+c_update_variably_modified (tree t)
+{
+ for (tree x = t; x; x = TYPE_NEXT_VARIANT (x))
+ {
+ C_TYPE_VARIABLY_MODIFIED (x) = 1;
+ for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
+ c_update_variably_modified (p);
+ }
+}
+
+
/* Verify the argument of the counted_by attribute of each field of
the containing structure, OUTMOST_STRUCT_TYPE, including its inner
anonymous struct/union, Report error and remove the corresponding
C_TYPE_VARIABLE_SIZE (t) = 1;
/* If any field is variably modified, record this fact. */
- if (C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (x)))
+ if (c_type_variably_modified_p (TREE_TYPE (x)))
C_TYPE_VARIABLY_MODIFIED (t) = 1;
if (DECL_C_BIT_FIELD (x))
finish_incomplete_vars (incomplete_vars, toplevel);
- /* Make sure a DECL_EXPR is created for structs with VLA members.
- Because we do not know the context, we always pass expr
- to force creation of a BIND_EXPR which is required in some
- contexts. */
+
if (c_type_variably_modified_p (t))
- add_decl_expr (loc, t, expr, false);
+ {
+ c_update_variably_modified (t);
+ /* Make sure a DECL_EXPR is created for structs with VLA members.
+ Because we do not know the context, we always pass expr
+ to force creation of a BIND_EXPR which is required in some
+ contexts. */
+ add_decl_expr (loc, t, expr, false);
+ }
if (warn_cxx_compat)
warn_cxx_compat_finish_struct (fieldlist, TREE_CODE (t), loc);
bool
c_var_mod_p (tree x, tree fn ATTRIBUTE_UNUSED)
{
- return C_TYPE_VARIABLY_MODIFIED (x);
+ return c_type_variably_modified_p (x);
}
/* Special routine to get the alias set of T for C. */
inline bool
c_type_variably_modified_p (tree t)
{
- return error_mark_node != t && C_TYPE_VARIABLY_MODIFIED (t);
+ if (error_mark_node == t)
+ return false;
+ if (C_TYPE_VARIABLY_MODIFIED (t))
+ return true;
+ if (TYPE_STRUCTURAL_EQUALITY_P (t))
+ {
+ /* The flag may not have been set yet because of incomplete
+ structure or union types completed later. */
+ switch (TREE_CODE (t))
+ {
+ case ARRAY_TYPE:
+ case FUNCTION_TYPE:
+ case POINTER_TYPE:
+ /* Recurse. */
+ if (c_type_variably_modified_p (TREE_TYPE (t)))
+ {
+ C_TYPE_VARIABLY_MODIFIED (t) = 1;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
}
inline bool
{
case POINTER_TYPE:
case FUNCTION_TYPE:
- /* Pointer and funcions can not have variable size. */
- if (C_TYPE_VARIABLE_SIZE (type))
+ case ARRAY_TYPE:
+ /* Pointer, array, functions are variably modified if and only if the
+ target, element, return type is variably modified. */
+ if (!TYPE_STRUCTURAL_EQUALITY_P (type)
+ && (C_TYPE_VARIABLY_MODIFIED (type)
+ != C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (type))))
return false;
- /* Pointer and funcions are variably modified if and only if the
- return / target type is variably modified. */
- if (C_TYPE_VARIABLY_MODIFIED (type)
- != C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (type)))
+ /* If the target type is structural equality, the type should also. */
+ if (!TYPE_STRUCTURAL_EQUALITY_P (type)
+ && TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (type)))
+ return false;
+
+ default:
+ break;
+ }
+
+ switch (TREE_CODE (type))
+ {
+ case POINTER_TYPE:
+ case FUNCTION_TYPE:
+ /* Pointer and functions can not have variable size. */
+ if (C_TYPE_VARIABLE_SIZE (type))
return false;
break;
case ARRAY_TYPE:
if (!C_TYPE_VARIABLY_MODIFIED (type))
return false;
}
- /* If the element type is variably modified, then also the array. */
- if (C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (type))
- && !C_TYPE_VARIABLY_MODIFIED (type))
- return false;
- break;
default:
break;
}
{
gcc_checking_assert (c_verify_type (old_type));
- if (C_TYPE_VARIABLY_MODIFIED (old_type))
+ if (c_type_variably_modified_p (old_type))
C_TYPE_VARIABLY_MODIFIED (new_type) = true;
if (TREE_CODE (new_type) == ARRAY_TYPE && C_TYPE_VARIABLE_SIZE (old_type))
/* Filter out the cases where referencing a non-local variable does not
require a non-local context passed via the static chain. */
- if (!C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (ref)))
+ if (!c_type_variably_modified_p (TREE_TYPE (ref)))
switch (TREE_CODE (ref))
{
case FUNCTION_DECL:
/* In this improbable scenario, a nested function returns a VM type.
Create a TARGET_EXPR so that the call always has a LHS, much as
what the C++ FE does for functions returning non-PODs. */
- if (C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (fntype)))
+ if (c_type_variably_modified_p (TREE_TYPE (fntype)))
{
tree tmp = create_tmp_var_raw (TREE_TYPE (fntype));
result = build4 (TARGET_EXPR, TREE_TYPE (fntype), tmp, result,
gcc_checking_assert (C_TYPE_VARIABLE_SIZE (var_type)
== C_TYPE_VARIABLE_SIZE (type));
- gcc_checking_assert (C_TYPE_VARIABLY_MODIFIED (var_type)
- == C_TYPE_VARIABLY_MODIFIED (type));
+ gcc_checking_assert (c_type_variably_modified_p (var_type)
+ == c_type_variably_modified_p (type));
/* A variant type does not inherit the list of incomplete vars from the
type main variant. */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23" } */
+
+#define NEST(...) typeof(*({ __VA_ARGS__ tmp = { }; &tmp; }))
+
+int e(int n)
+{
+ struct foo { char buf[n]; } *p;
+ {
+ struct foo { char buf[n]; } *q;
+ 1 ? p : q;
+ }
+}
+
+int f(int n)
+{
+ typedef struct foo bar;
+ struct foo { NEST(struct foo { bar *x; char buf[n]; }) *x; char buf[n]; } *q;
+ typeof(q->x) p0;
+ typeof(q->x) p1;
+ 1 ? p0 : q;
+ 1 ? p1 : q;
+ 1 ? p0 : p1;
+}
+
+int g(int n)
+{
+ typedef struct fo2 bar;
+ struct fo2 { NEST(struct fo2 { NEST(struct fo2 { bar *x; char buf[n]; }) * x; char buf[n]; }) *x; char buf[n]; } *q;
+ typeof(q->x) p0;
+ typeof(q->x->x) p1;
+ typeof(q->x->x->x) p2;
+ 1 ? p0 : q;
+ 1 ? p1 : q;
+ 1 ? p2 : q;
+ 1 ? p0 : p1;
+ 1 ? p2 : p1;
+ 1 ? p0 : p2;
+}
+
+int h0(int n)
+{
+ typedef struct foo bar;
+ struct foo { NEST(struct foo { bar *x; char buf[3]; }) *x; char buf[n]; } *q;
+ typeof(q->x) p0;
+ typeof(q->x) p1;
+ 1 ? p0 : q;
+ 1 ? p1 : q;
+ 1 ? p0 : p1;
+}
+
+int h1(int n)
+{
+ typedef struct foo bar;
+ struct foo { NEST(struct foo { bar *x; char buf[n]; }) *x; char buf[3]; } *q;
+ typeof(q->x) p0;
+ typeof(q->x) p1;
+ 1 ? p0 : q;
+ 1 ? p1 : q;
+ 1 ? p0 : p1;
+}
+
+int h2(int n)
+{
+ typedef struct foo bar;
+ struct foo { NEST(struct foo { bar *x; char buf[n]; }) *x; char buf[ ]; } *q;
+ typeof(q->x) p0;
+ typeof(q->x) p1;
+ 1 ? p0 : q;
+ 1 ? p1 : q;
+ 1 ? p0 : p1;
+}
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23" } */
+
+void f1(int n)
+{
+ struct foo b();
+ struct foo {
+ char buf[n];
+ };
+ goto out; /* { dg-error "jump into scope" } */
+ typeof(b) *t;
+out:
+}
+
+void f2(int n)
+{
+ struct foo *b[1];
+ struct foo {
+ char buf[n];
+ };
+ goto out; /* { dg-error "jump into scope" } */
+ typeof(b) *t;
+out:
+}
+
+void f3(int n)
+{
+ struct foo (*b[1])();
+ struct foo {
+ char buf[n];
+ };
+ goto out; /* { dg-error "jump into scope" } */
+ typeof(b) *t;
+out:
+}
+
+
+void f4(int n)
+{
+ const struct foo * const * const c;
+ struct foo {
+ char buf[n];
+ };
+ goto out; /* { dg-error "jump into scope" } */
+ typeof(c) *t;
+out:
+}
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+void bar(const struct S *x, int y) { /* { dg-warning "not be visible" } */
+ const struct S {
+ int d[y];
+ } *a = x;
+};
+