diff options
-rw-r--r-- | gcc/c/c-decl.cc | 51 | ||||
-rw-r--r-- | gcc/c/c-objc-common.cc | 5 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 1 | ||||
-rw-r--r-- | gcc/c/c-typeck.cc | 31 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-2.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-5.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-alias-1.c | 49 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-alias-2.c | 50 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-alias-3.c | 32 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-alias-4.c | 32 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-alias-5.c | 36 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c | 33 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c | 85 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c | 83 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c | 36 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c | 107 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c | 60 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c | 93 |
18 files changed, 785 insertions, 3 deletions
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 26188aa..6639ec3 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -634,6 +634,36 @@ public: auto_vec<tree> typedefs_seen; }; + +/* Hash table for structs and unions. */ +struct c_struct_hasher : ggc_ptr_hash<tree_node> +{ + static hashval_t hash (tree t); + static bool equal (tree, tree); +}; + +/* Hash an RECORD OR UNION. */ +hashval_t +c_struct_hasher::hash (tree type) +{ + inchash::hash hstate; + + hstate.add_int (TREE_CODE (type)); + hstate.add_object (TYPE_NAME (type)); + + return hstate.end (); +} + +/* Compare two RECORD or UNION types. */ +bool +c_struct_hasher::equal (tree t1, tree t2) +{ + return comptypes_equiv_p (t1, t2); +} + +/* All tagged typed so that TYPE_CANONICAL can be set correctly. */ +static GTY (()) hash_table<c_struct_hasher> *c_struct_htab; + /* Information for the struct or union currently being parsed, or NULL if not parsing a struct or union. */ static class c_struct_parse_info *struct_parse_info; @@ -8713,7 +8743,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name, ref = lookup_tag (code, name, has_enum_type_specifier, &refloc); /* If the visble type is still being defined, see if there is - an earlier definition (which may be complete). */ + an earlier definition (which may be complete). We do not + have to loop because nested redefinitions are not allowed. */ if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref)) { tree vis = previous_tag (ref); @@ -9661,6 +9692,24 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, C_TYPE_BEING_DEFINED (t) = 0; + /* Set type canonical based on equivalence class. */ + if (flag_isoc23) + { + if (NULL == c_struct_htab) + c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61); + + hashval_t hash = c_struct_hasher::hash (t); + + tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT); + if (*e) + TYPE_CANONICAL (t) = *e; + else + { + TYPE_CANONICAL (t) = t; + *e = t; + } + } + tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t)); for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) { diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc index 53eda7f..a394627 100644 --- a/gcc/c/c-objc-common.cc +++ b/gcc/c/c-objc-common.cc @@ -422,6 +422,11 @@ c_get_alias_set (tree t) if (TREE_CODE (t) == ENUMERAL_TYPE) return get_alias_set (ENUM_UNDERLYING_TYPE (t)); + /* Structs with variable size can alias different incompatible + structs. Let them alias anything. */ + if (RECORD_OR_UNION_TYPE_P (t) && C_TYPE_VARIABLE_SIZE (t)) + return 0; + return c_common_get_alias_set (t); } diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 54f1353..02a09e5 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -759,6 +759,7 @@ extern tree require_complete_type (location_t, tree); extern bool same_translation_unit_p (const_tree, const_tree); extern int comptypes (tree, tree); extern bool comptypes_same_p (tree, tree); +extern bool comptypes_equiv_p (tree, tree); extern int comptypes_check_different_types (tree, tree, bool *); extern int comptypes_check_enum_int (tree, tree, bool *); extern bool c_mark_addressable (tree, bool = false); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 0dbb0b4..1c81729 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -1063,6 +1063,7 @@ struct comptypes_data { bool different_types_p; bool warning_needed; bool anon_field; + bool equiv; const struct tagged_tu_seen_cache* cache; }; @@ -1123,6 +1124,21 @@ comptypes_check_different_types (tree type1, tree type2, return ret ? (data.warning_needed ? 2 : 1) : 0; } + + +/* Like comptypes, but if it returns nonzero for struct and union + types considered equivalent for aliasing purposes. */ + +bool +comptypes_equiv_p (tree type1, tree type2) +{ + struct comptypes_data data = { }; + data.equiv = true; + bool ret = comptypes_internal (type1, type2, &data); + + return ret; +} + /* Return true if TYPE1 and TYPE2 are compatible types for assignment or various other operations. If they are compatible but a warning may @@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2, if ((d1 == NULL_TREE) != (d2 == NULL_TREE)) data->different_types_p = true; + /* Ignore size mismatches. */ + if (data->equiv) + return true; /* Sizes must match unless one is missing or variable. */ if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2) return true; @@ -1467,6 +1486,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2))) return false; + if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2))) + return false; + for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2); s1 && s2; s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2)) @@ -1486,6 +1508,15 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, && simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1), DECL_FIELD_BIT_OFFSET (s2)) != 1) return false; + + tree st1 = TYPE_SIZE (TREE_TYPE (s1)); + tree st2 = TYPE_SIZE (TREE_TYPE (s2)); + + if (data->equiv + && st1 && TREE_CODE (st1) == INTEGER_CST + && st2 && TREE_CODE (st2) == INTEGER_CST + && !tree_int_cst_equal (st1, st2)) + return false; } return true; diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c index 5dd4a21..444605a 100644 --- a/gcc/testsuite/gcc.dg/c23-tag-2.c +++ b/gcc/testsuite/gcc.dg/c23-tag-2.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target { ! "*-*-*" } } } +/* { dg-do compile } * { dg-options "-std=c23" } */ diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c index ff7bbd6..9011126 100644 --- a/gcc/testsuite/gcc.dg/c23-tag-5.c +++ b/gcc/testsuite/gcc.dg/c23-tag-5.c @@ -1,4 +1,4 @@ -/* { dg-do run { target { ! "*-*-*" } } } +/* { dg-do run } * { dg-options "-std=c23 -fpermissive" } */ diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c new file mode 100644 index 0000000..c92f942 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c @@ -0,0 +1,49 @@ +/* { dg-do run } + * { dg-options "-std=c23 -O2" } + */ + + +/* These tests check that redefinitions of tagged + types can alias the original definitions. */ + +struct foo { int x; }; + +int test_foo(struct foo* a, void* b) +{ + a->x = 1; + + struct foo { int x; }* p = b; + p->x = 2; + + return a->x; +} + + +enum bar { A = 1, B = 3 }; + +int test_bar(enum bar* a, void* b) +{ + *a = A; + + enum bar { A = 1, B = 3 }* p = b; + *p = B; + + return *a; +} + + +int main() +{ + struct foo y; + + if (2 != test_foo(&y, &y)) + __builtin_abort(); + + enum bar z; + + if (B != test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c new file mode 100644 index 0000000..64ff67d --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c @@ -0,0 +1,50 @@ +/* { dg-do run } + * { dg-options "-std=c23 -flto -O2" } + */ + +/* These tests check that compatible definitions of + tagged types can alias the original definitions + with LTO. */ + +struct foo { int x; }; + +int test_foo(struct foo* a, void* b) +{ + a->x = 1; + + struct foo { int x; }* p = b; + p->x = 2; + + return a->x; +} + + +enum bar { A = 1, B = 3 }; + +int test_bar(enum bar* a, void* b) +{ + *a = A; + + enum bar { A = 1, B = 3 }* p = b; + *p = B; + + return *a; +} + + +int main() +{ + struct foo y; + + if (2 != test_foo(&y, &y)) + __builtin_abort(); + + enum bar z; + + if (B != test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-3.c b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c new file mode 100644 index 0000000..b9fe6f3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c @@ -0,0 +1,32 @@ +/* { dg-do run } + * { dg-options "-std=c23 -O2" } + */ + +/* These tests check that definitions of enums with + * the same underlying type can alias, even when + * they are not compatible. */ + +enum bar : long { A = 1, B = 3 }; + +int test_bar(enum bar* a, void* b) +{ + *a = A; + + enum foo : long { C = 2, D = 4 }* p = b; + *p = B; + + return *a; +} + + +int main() +{ + enum bar z; + + if (B != test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-4.c b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c new file mode 100644 index 0000000..1d43d0d --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c @@ -0,0 +1,32 @@ +/* { dg-do run } + * { dg-options "-std=c23 -O2" } + */ + + +/* Here we check that structs with flexible array + * members can alias a compatible redefinition. */ + +struct bar { int x; int f[]; }; + +int test_bar1(struct bar* a, void* b) +{ + a->x = 1; + + struct bar { int x; int f[]; }* p = b; + struct bar* q = a; + p->x = 2; + + return a->x; +} + +int main() +{ + struct bar z; + + if (2 != test_bar1(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c new file mode 100644 index 0000000..f5cfad1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c @@ -0,0 +1,36 @@ +/* { dg-do compile } + * { dg-options "-std=c23 -O2" } + */ + +/* The structs are incompatible so can be assumed not to + * alias, but this is not exploited. So do not check for + * this below but check the error about incompatibility. */ + +struct bar { int x; int f[]; }; + +int test_bar3(struct bar* a, void* b) +{ + a->x = 1; + + struct bar { int x; int f[1]; }* p = b; + struct bar* q = a; /* { dg-error "incompatible" } */ + p->x = 2; + + return a->x; +} + + +int main() +{ + struct bar z; + + // allow both results here + int r = test_bar3(&z, &z); + + // UB but could be expected to return 1 with optimization + // exploiting the UB (not done at time of writing) or 2 + + return r; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c new file mode 100644 index 0000000..c51417f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c @@ -0,0 +1,33 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -O2" } + */ + +/* Check that structs with flexible array member can alias. */ + +struct bar { int x; int f[]; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar2(struct bar* a, void* b) +{ + a->x = 1; + + struct bar { int x; int f[0]; }* p = b; + struct bar* q = a; + p->x = 2; + + return a->x; +} + + + +int main() +{ + struct bar z; + + if (2 != test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c new file mode 100644 index 0000000..c09c3ca --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c @@ -0,0 +1,85 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -O2" } + */ + + +/* These tests check that incompatible definitions of + tagged types can be assumed not to alias and that + this is exploited during optimization. */ + +struct foo { int x; }; + +[[gnu::noinline,gnu::noipa]] +int test_foo1(struct foo* a, void* b) +{ + a->x = 1; + + struct foo { int x; int y; }* p = b; + p->x = 2; + + return a->x; +} + +[[gnu::noinline,gnu::noipa]] +int test_foo2(struct foo* a, void* b) +{ + a->x = 1; + + struct fox { int x; }* p = b; + p->x = 2; + + return a->x; +} + + + +/* While these tests check that incompatible enums can still + * alias, although this is not required. */ + +enum bar { A = 1, B = 3, C = 5, D = 9 }; + +[[gnu::noinline,gnu::noipa]] +int test_bar1(enum bar* a, void* b) +{ + *a = A; + + enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b; + *p = B; + + return *a; +} + +[[gnu::noinline,gnu::noipa]] +int test_bar2(enum bar* a, void* b) +{ + *a = A; + + enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b; + *p = B; + + return *a; +} + + +int main() +{ + struct foo y; + + if (1 != test_foo1(&y, &y)) + __builtin_abort(); + + if (1 != test_foo2(&y, &y)) + __builtin_abort(); + + enum bar z; + + if (B != test_bar1(&z, &z)) + __builtin_abort(); + + if (B != test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c new file mode 100644 index 0000000..c2fd4e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c @@ -0,0 +1,83 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -flto -O2" } + */ + +/* These tests check that incompatible definitions of + tagged types can be assumed not to alias and that + this is exploited during optimization with LTO. */ + +struct foo { int x; }; + +[[gnu::noinline,gnu::noipa]] +int test_foo1(struct foo* a, void* b) +{ + a->x = 1; + + struct foo { int x; int y; }* p = b; + p->x = 2; + + return a->x; +} + +[[gnu::noinline,gnu::noipa]] +int test_foo2(struct foo* a, void* b) +{ + a->x = 1; + + struct fox { int x; }* p = b; + p->x = 2; + + return a->x; +} + + +/* While these tests check that incompatible definitions + * of enums can alias. */ + +enum bar { A = 1, B = 3, C = 5, D = 9 }; + +[[gnu::noinline,gnu::noipa]] +int test_bar1(enum bar* a, void* b) +{ + *a = A; + + enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b; + *p = B; + + return *a; +} + +[[gnu::noinline,gnu::noipa]] +int test_bar2(enum bar* a, void* b) +{ + *a = A; + + enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b; + *p = B; + + return *a; +} + + +int main() +{ + struct foo y; + + if (1 != test_foo1(&y, &y)) + __builtin_abort(); + + if (1 != test_foo2(&y, &y)) + __builtin_abort(); + + enum bar z; + + if (B != test_bar1(&z, &z)) + __builtin_abort(); + + if (B != test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c new file mode 100644 index 0000000..1ea3a88 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c @@ -0,0 +1,36 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -O2" } + */ + +/* This test checks that an incompatible definition of + * a tagged type without tag can be assumed not to alias. + * and that this is exploited during optimization. */ + + +// not sure this is wise, but this was already like this before + +typedef struct { int x; } foo_t; + +[[gnu::noinline,gnu::noipa]] +int test_foo(foo_t* a, void* b) +{ + a->x = 1; + + struct { int x; }* p = b; + p->x = 2; + + return a->x; +} + + +int main() +{ + foo_t y; + + if (1 != test_foo(&y, &y)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c new file mode 100644 index 0000000..5a83397 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c @@ -0,0 +1,107 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -O2" } + */ + +/* This test checks that different field offsets imply + * that the types can be assumed not to alias + * and that this is exploited during optimization. */ + + +struct bar0 { int x; int f[3]; int y; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar0(struct bar0* a, void* b) +{ + a->x = 1; + + struct bar0 { int x; int f[4]; int y; }* p = b; + p->x = 2; + + return a->x; +} + + +/* While these tests check that different structs with different + * sizes in arrays pointed to by field members can alias, + * even though the types are incompatible. */ + + +struct bar1 { int x; int (*f)[3]; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar1(struct bar1* a, void* b) +{ + a->x = 1; + + struct bar1 { int x; int (*f)[3]; }* p = b; + p->x = 2; + + return a->x; +} + + +struct bar2 { int x; int (*f)[3]; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar2(struct bar2* a, void* b) +{ + a->x = 1; + + struct bar2 { int x; int (*f)[4]; }* p = b; + p->x = 2; + + return a->x; +} + + + +/* This test checks that different structs with pointers to + * different compatible arrays types can alias. */ + + +struct bar3 { int x; int (*f)[3]; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar3(struct bar3* a, void* b) +{ + a->x = 1; + + struct bar3 { int x; int (*f)[]; }* p = b; + p->x = 2; + + return a->x; +} + + + + +int main() +{ + // control + + struct bar0 z0; + + if (1 != test_bar0(&z0, &z0)) + __builtin_abort(); + + // this could be different + struct bar1 z1; + + if (2 != test_bar1(&z1, &z1)) + __builtin_abort(); + + struct bar2 z2; + + if (2 != test_bar2(&z2, &z2)) + __builtin_abort(); + + struct bar3 z3; + + if (2 != test_bar3(&z3, &z3)) + __builtin_abort(); + + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c new file mode 100644 index 0000000..78d2abf --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c @@ -0,0 +1,60 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -O2" } + */ + + + +/* Here we check that struct with a variable size (GNU extension) + * can alias a struct with a flexible array member or a struct with a + * fixed size array as last element. */ + +struct bar { int x; int f[]; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar4(struct bar* a, void* b) +{ + a->x = 1; + + int n = 3; + struct bar { int x; int f[n]; }* p = b; + struct bar* q = a; + p->x = 2; + + return a->x; +} + + +struct foo { int x; int f[3]; }; + + +[[gnu::noinline,gnu::noipa]] +int test_foo1(struct foo* a, void* b) +{ + a->x = 1; + + int n = 3; + struct foo { int x; int f[n]; }* p = b; + struct foo* q = a; + p->x = 2; + + return a->x; +} + + + +int main() +{ + struct bar z; + + if (2 != test_bar4(&z, &z)) + __builtin_abort(); + + struct foo y; + + if (2 != test_foo1(&y, &y)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c new file mode 100644 index 0000000..d3fc4bd --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c @@ -0,0 +1,93 @@ +/* { dg-do run } + * { dg-options "-std=gnu23 -O2" } + */ + + +/* We check that the incompatible enums as fields lead to + * incompatible types that can be assumed not to alias + * and that this is exploited during optimization. */ + +struct bar1 { int x; enum A1 { X1 = 1 } f; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar1(struct bar1* a, void* b) +{ + a->x = 1; + + struct bar1 { int x; enum A1 { X1 = 2 } f; }* p = b; + p->x = 2; + + return a->x; +} + + +struct bar2 { int x; enum A2 { X2 = 1 } f; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar2(struct bar2* a, void* b) +{ + a->x = 1; + + struct bar2 { int x; enum B2 { X2 = 1 } f; }* p = b; + p->x = 2; + + return a->x; +} + + + +struct bar3 { int x; enum A3 { X3 = 1 } f; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar3(struct bar3* a, void* b) +{ + a->x = 1; + + struct bar3 { int x; enum A3 { Y3 = 1 } f; }* p = b; + p->x = 2; + + return a->x; +} + + +struct bar4 { int x; enum { Z4 = 1 } f; }; + +[[gnu::noinline,gnu::noipa]] +int test_bar4(struct bar4* a, void* b) +{ + a->x = 1; + + struct bar4 { int x; enum { Z4 = 1 } f; }* p = b; + p->x = 2; + + return a->x; +} + + + +int main() +{ + struct bar1 z1; + + if (1 != test_bar1(&z1, &z1)) + __builtin_abort(); + + struct bar2 z2; + + if (1 != test_bar2(&z2, &z2)) + __builtin_abort(); + + struct bar3 z3; + + if (1 != test_bar3(&z3, &z3)) + __builtin_abort(); + + struct bar4 z4; + + if (1 != test_bar4(&z4, &z4)) + __builtin_abort(); + + return 0; +} + + |