diff options
author | Martin Uecker <uecker@tugraz.at> | 2024-03-30 19:49:48 +0100 |
---|---|---|
committer | Martin Uecker <uecker@tugraz.at> | 2024-05-29 22:23:00 +0200 |
commit | 86b98d939989427ff025bcfd536ad361fcdc699c (patch) | |
tree | a8f902b52285a418806953eefb73a34b32785650 /gcc | |
parent | 915440eed21de367cb41857afb5273aff5bcb737 (diff) | |
download | gcc-86b98d939989427ff025bcfd536ad361fcdc699c.zip gcc-86b98d939989427ff025bcfd536ad361fcdc699c.tar.gz gcc-86b98d939989427ff025bcfd536ad361fcdc699c.tar.bz2 |
C23: fix aliasing for structures/unions with incomplete types
When incomplete structure/union types are completed later, compatibility
of struct types that contain pointers to such types changes. When forming
equivalence classes for TYPE_CANONICAL, we therefor need to be conservative
and treat all structs with the same tag which are pointer targets as
equivalent for purposed of determining equivalency of structure/union
types which contain such types as member. This avoids having to update
TYPE_CANONICAL of such structure/unions recursively. The pointer types
themselves are updated in c_update_type_canonical.
gcc/c/
* c-typeck.cc (comptypes_internal): Add flag to track
whether a struct is the target of a pointer.
(tagged_types_tu_compatible): When forming equivalence
classes, treat nested pointed-to structs as equivalent.
gcc/testsuite/
* gcc.dg/c23-tag-incomplete-alias-1.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/c/c-typeck.cc | 43 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c | 36 |
2 files changed, 76 insertions, 3 deletions
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index ad4c7ad..09b2c265 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -1172,6 +1172,7 @@ struct comptypes_data { bool different_types_p; bool warning_needed; bool anon_field; + bool pointedto; bool equiv; const struct tagged_tu_seen_cache* cache; @@ -1235,8 +1236,36 @@ comptypes_check_different_types (tree type1, tree type2, } -/* Like comptypes, but if it returns nonzero for struct and union - types considered equivalent for aliasing purposes. */ +/* Like comptypes, but if it returns true for struct and union types + considered equivalent for aliasing purposes, i.e. for setting + TYPE_CANONICAL after completing a struct or union. + + This function must return false only for types which are not + compatible according to C language semantics (cf. comptypes), + otherwise the middle-end would make incorrect aliasing decisions. + It may return true for some similar types that are not compatible + according to those stricter rules. + + In particular, we ignore size expression in arrays so that the + following structs are in the same equivalence class: + + struct foo { char (*buf)[]; }; + struct foo { char (*buf)[3]; }; + struct foo { char (*buf)[4]; }; + + We also treat unions / structs with members which are pointers to + structures or unions with the same tag as equivalent (if they are not + incompatible for other reasons). Although incomplete structure + or union types are not compatible to any other type, they may become + compatible to different types when completed. To avoid having to update + TYPE_CANONICAL at this point, we only consider the tag when forming + the equivalence classes. For example, the following types with tag + 'foo' are all considered equivalent: + + struct bar; + struct foo { struct bar *x }; + struct foo { struct bar { int a; } *x }; + struct foo { struct bar { char b; } *x }; */ bool comptypes_equiv_p (tree type1, tree type2) @@ -1357,6 +1386,7 @@ comptypes_internal (const_tree type1, const_tree type2, /* Do not remove mode information. */ if (TYPE_MODE (t1) != TYPE_MODE (t2)) return false; + data->pointedto = true; return comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data); case FUNCTION_TYPE: @@ -1375,7 +1405,7 @@ comptypes_internal (const_tree type1, const_tree type2, if ((d1 == NULL_TREE) != (d2 == NULL_TREE)) data->different_types_p = true; - /* Ignore size mismatches. */ + /* Ignore size mismatches when forming equivalence classes. */ if (data->equiv) return true; /* Sizes must match unless one is missing or variable. */ @@ -1515,6 +1545,12 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, if (TYPE_NAME (t1) != TYPE_NAME (t2)) return false; + /* When forming equivalence classes for TYPE_CANONICAL in C23, we treat + structs with the same tag as equivalent, but only when they are targets + of pointers inside other structs. */ + if (data->equiv && data->pointedto) + return true; + if (!data->anon_field && NULL_TREE == TYPE_NAME (t1)) return false; @@ -1610,6 +1646,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, return false; data->anon_field = !DECL_NAME (s1); + data->pointedto = false; data->cache = &entry; if (!comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data)) diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c new file mode 100644 index 0000000..f9ec486 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c @@ -0,0 +1,36 @@ +/* { dg-do run } + * { dg-options "-std=c23 -O2" } */ + +[[gnu::noinline]] +void *alias(void *ap, void *bp, void *x, void *y) +{ + struct foo { struct bar *f; } *a = ap; + struct bar { long x; }; + + a->f = x; + + { + struct bar; + struct foo { struct bar *f; } *b = bp; + struct bar { long x; }; + + // after completing bar, the two struct foo should be compatible + + b->f = y; + } + + + return a->f; +} + +int main() +{ + struct bar { long x; }; + struct foo { struct bar *f; } a; + struct bar x, y; + if (&y != alias(&a, &a, &x, &y)) + __builtin_abort(); + + return 0; +} + |