Hello Joseph and all,
here is a preliminary patch the implements the proposed tag compatibility rules for C23 in GCC (N2863). It works by tweaking the diagnostics in the FE and by recording structs/union types to be able to set TYPE_CANONICAL to a canonical definition (as per previous discussions). Overall, this seems to work very well when testing on my own projects. There are still some issues left that I want to point out: - at the moment, all struct/union types are collected in a vector. This needs to be replaced by a hash table. - the feature has a flag (-ftag-compat) which is now turned on by default in all language modes to facilitate testing and to identify backwards compatibility problems. Turned on, it survives bootstrapping and regression testing with only a few cases that test for diagnostics that go away changed to turn it off. - The new rules are not applied to structs with variable sized members (which are a GNU extension). - In contrast to the published proposal, structs without tags are now treated as incompatible as requested by WG14. - There is still one assertion in ipa-free-lang-data I had to conditionally turn off and did not have time to investigate. Otherwise, there are only C FE changes. LTO may still need some more testing. - It fixes some bugs in (formerly) unused FE code and removes some other dead code. This could be moved into its own patch. - If adopted into C, I assume we need some compatibility warnings. From testing, I could not identify any backwards compatibility problems. - There are certainly some issues I may have overlooked. Martin gcc/ * c-family/c.opt: Add -ftag-compat flag. * c/c-decl.cc (pop_scope): Remove dead code. (diagnose_mismatched_decls): Support for new tag compatibility rules. (start_struct): Dito. (finish_struct): Dito. (start_enum): Dito. (finish_enum): Dito. (build_enumerator): Pass enumtype to build_decl. (c_simulate_enum_decl): Pass enumtype to build_enumerator. * c/c-parser.cc (c_parser_enum_specifier): Dito. * c/c-tree.h (build_enumerator): Add enumtype argument. * c/c-typeck.cc (comptypes_internal): Support for new tag compatibility rules. (same_translation_unit_p): Removed. (tagged_types_tu_compatible_p): Bug fixes and support for new tag compatibility rules. (convert_for_assignment): Support for new tag compatibility rules (digest_init): Dito. * ipa-free-lang-data.cc (fld_incomplete_type_of): Conditionally turn of assertion related to TYPE_CANONICAL if -ftag-compat is on. doc/ * invoke.texi: Document -ftag-compat flag. testsuite/ * gcc.dg/asan/pr81460.c: Add -fno-tag-compat. * gcc.dg/c99-tag-1.c: Add -fno-tag-compat. * gcc.dg/c99-tag-2.c: Add -fno-tag-compat. * gcc.dg/decl-3.c: Add -fno-tag-compat. * gcc.dg/enum-redef-1.c: Add -fno-tag-compat. * gcc.dg/parm-incomplete-1.c: Add -fno-tag-compat. * gcc.dg/pr17188-1.c: Add -fno-tag-compat. * gcc.dg/pr18809-1.c: Add -pedantic-errors and -fno-tag-compat. * gcc.dg/pr27953.c: Add -fno-tag-compat. * gcc.dg/pr39084.c: Add -fno-tag-compat. * gcc.dg/pr68533.c: Add -fno-tag-compat. * gcc.dg/pr79983.c: Add -fno-tag-compat. * gcc.dg/pr89211.c: Add -fno-tag-compat. * gcc.dg/tag-compat.c: New test. * gcc.dg/tag-compat10.c: New test. * gcc.dg/tag-compat11.c: New test. * gcc.dg/tag-compat12.c: New test. * gcc.dg/tag-compat2.c: New test. * gcc.dg/tag-compat3.c: New test. * gcc.dg/tag-compat4.c: New test. * gcc.dg/tag-compat5.c: New test. * gcc.dg/tag-compat6.c: New test. * gcc.dg/tag-compat7.c: New test. * gcc.dg/tag-compat8.c: New test. * gcc.dg/tag-compat9.c: New test. * gcc.dg/vla-11.c: Add -fno-tag-compat. * gcc.dg/vla-stexp-2.c: Add -fno-tag-compat. diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 41a20bc625e..cd3164018f2 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -2108,6 +2108,9 @@ Enum(strong_eval_order) String(some) Value(1) EnumValue Enum(strong_eval_order) String(all) Value(2) +ftag-compat +C Var(flag_tag_compat) Init(1) + ftemplate-backtrace-limit= C++ ObjC++ Joined RejectNegative UInteger Var(template_backtrace_limit) Init(10) Set the maximum number of template instantiation notes for a single warning or error. diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 5266a61b859..df208a310f9 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -599,6 +599,10 @@ public: auto_vec<tree> typedefs_seen; }; + +/* All tagged typed so that TYPE_CANONICAL can be set correctly. */ +static auto_vec<tree> all_structs; + /* 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; @@ -1354,8 +1358,8 @@ pop_scope (void) BLOCK_VARS (block) = extp; } /* If this is the file scope set DECL_CONTEXT of each decl to - the TRANSLATION_UNIT_DECL. This makes same_translation_unit_p - work. */ + the TRANSLATION_UNIT_DECL. */ + if (scope == file_scope) { DECL_CONTEXT (p) = context; @@ -1985,9 +1989,22 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, given scope. */ if (TREE_CODE (olddecl) == CONST_DECL) { - auto_diagnostic_group d; - error ("redeclaration of enumerator %q+D", newdecl); - locate_old_decl (olddecl); + if (flag_tag_compat && comptypes (TREE_TYPE (olddecl), TREE_TYPE (newdecl))) + { + if (!COMPLETE_TYPE_P (TREE_TYPE (olddecl)) + || !simple_cst_equal (DECL_INITIAL (olddecl), DECL_INITIAL (newdecl))) + { + auto_diagnostic_group d; + error ("conflicting redeclaration of enumerator %q+D", newdecl); + locate_old_decl (olddecl); + } + } + else + { + auto_diagnostic_group d; + error ("redeclaration of enumerator %q+D", newdecl); + locate_old_decl (olddecl); + } return false; } @@ -2238,7 +2255,7 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, isn't overriding an extern inline reject the new decl. In c99, no overriding is allowed in the same translation unit. */ - if ((!DECL_EXTERN_INLINE (olddecl) + if (!DECL_EXTERN_INLINE (olddecl) || DECL_EXTERN_INLINE (newdecl) || (!flag_gnu89_inline && (!DECL_DECLARED_INLINE_P (olddecl) @@ -2248,7 +2265,6 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, || !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (newdecl)))) ) - && same_translation_unit_p (newdecl, olddecl)) { auto_diagnostic_group d; error ("redefinition of %q+D", newdecl); @@ -3267,18 +3283,11 @@ pushdecl (tree x) type to the composite of all the types of that declaration. After the consistency checks, it will be reset to the composite of the visible types only. */ - if (b && (TREE_PUBLIC (x) || same_translation_unit_p (x, b- >decl)) - && b->u.type) + if (b && b->u.type) TREE_TYPE (b->decl) = b->u.type; - /* The point of the same_translation_unit_p check here is, - we want to detect a duplicate decl for a construct like - foo() { extern bar(); } ... static bar(); but not if - they are in different translation units. In any case, - the static does not go in the externals scope. */ - if (b - && (TREE_PUBLIC (x) || same_translation_unit_p (x, b->decl)) - && duplicate_decls (x, b->decl)) + /* The static does not go in the externals scope. */ + if (b && duplicate_decls (x, b->decl)) { tree thistype; if (vistype) @@ -8088,7 +8097,8 @@ get_parm_info (bool ellipsis, tree expr) (it's impossible to call such a function with type- correct arguments). An anonymous union parm type is meaningful as a GNU extension, so don't warn for that. */ - if (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE) + if (!flag_tag_compat + && (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE)) { if (b->id) /* The %s will be one of 'struct', 'union', or 'enum'. */ @@ -8288,6 +8298,12 @@ start_struct (location_t loc, enum tree_code code, tree name, if (name != NULL_TREE) ref = lookup_tag (code, name, true, &refloc); + + /* If we already have a completed definition, then + do not use it. We will check for consistency later */ + if (flag_tag_compat && ref && TYPE_SIZE (ref)) + ref = NULL_TREE; + if (ref && TREE_CODE (ref) == code) { if (TYPE_STUB_DECL (ref)) @@ -8977,6 +8993,54 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, warning_at (loc, 0, "union cannot be made transparent"); } + /* Check for consistency with previous definition */ + if (flag_tag_compat) + { + struct c_binding *b = NULL; + tree name = TYPE_NAME (t); + + if (name) + b = I_TAG_BINDING (name); + + if (b) + b = b->shadowed; + + if (b && B_IN_CURRENT_SCOPE (b)) + { + tree vistype = b->decl; + bool different_types = false; + + if ((1 != comptypes_check_different_types(t, vistype, &different_types)) + || different_types) + warning_at(loc, 0, "redefinition of struct or union %qT", vistype); + } + } + + if (flag_tag_compat) + { + gcc_assert (t == TYPE_MAIN_VARIANT (t)); + /* We treat structs with variable size as + incompatible with other structs. */ + if (C_TYPE_VARIABLE_SIZE (t)) + TYPE_CANONICAL (t) = t; + else + { + unsigned int i; + tree t2 = NULL_TREE; + FOR_EACH_VEC_ELT (all_structs, i, t2) + if (comptypes (t, t2)) + break; + + if (t2 != NULL_TREE) + TYPE_CANONICAL (t) = t2; + else + { + TYPE_CANONICAL (t) = t; + all_structs.safe_push (t); + } + } + } + tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t)); for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) { @@ -8987,6 +9051,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, C_TYPE_FIELDS_VOLATILE (x) = C_TYPE_FIELDS_VOLATILE (t); C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t); C_TYPE_INCOMPLETE_VARS (x) = NULL_TREE; + TYPE_CANONICAL (x) = TYPE_CANONICAL (t); } /* Update type location to the one of the definition, instead of e.g. @@ -8994,6 +9059,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, if (TYPE_STUB_DECL (t)) DECL_SOURCE_LOCATION (TYPE_STUB_DECL (t)) = loc; + /* Finish debugging output for this type. */ rest_of_type_compilation (t, toplevel); @@ -9100,9 +9166,15 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name) if (name != NULL_TREE) enumtype = lookup_tag (ENUMERAL_TYPE, name, true, &enumloc); + if (flag_tag_compat && enumtype != NULL_TREE + && TREE_CODE (enumtype) == ENUMERAL_TYPE + && TYPE_VALUES (enumtype) != NULL_TREE) + enumtype = NULL_TREE; + if (enumtype == NULL_TREE || TREE_CODE (enumtype) != ENUMERAL_TYPE) { enumtype = make_node (ENUMERAL_TYPE); + TYPE_SIZE (enumtype) = NULL_TREE; pushtag (loc, name, enumtype); } /* Update type location to the one of the definition, instead of e.g. @@ -9311,6 +9383,31 @@ finish_enum (tree enumtype, tree values, tree attributes) C_TYPE_BEING_DEFINED (enumtype) = 0; + /* Check for consistency with previous definition */ + + if (flag_tag_compat) + { + struct c_binding *b = NULL; + tree name = TYPE_NAME (enumtype); + + if (name) + b = I_TAG_BINDING (name); + + if (b) + b = b->shadowed; + + if (b && B_IN_CURRENT_SCOPE (b)) + { + tree vistype = b->decl; + bool different_types = false; + + if ((1 != comptypes_check_different_types(enumtype, vistype, &different_types)) + || different_types) + { + warning(0, "conflicting redefinition of enum %qT", vistype); + } + } + } return enumtype; } @@ -9322,7 +9419,7 @@ finish_enum (tree enumtype, tree values, tree attributes) Assignment of sequential values by default is handled here. */ tree -build_enumerator (location_t decl_loc, location_t loc, +build_enumerator (location_t decl_loc, location_t loc, tree enumtype, struct c_enum_contents *the_enum, tree name, tree value) { tree decl, type; @@ -9409,7 +9506,7 @@ build_enumerator (location_t decl_loc, location_t loc, >= TYPE_PRECISION (integer_type_node) && TYPE_UNSIGNED (type))); - decl = build_decl (decl_loc, CONST_DECL, name, type); + decl = build_decl (decl_loc, CONST_DECL, name, enumtype); DECL_INITIAL (decl) = convert (type, value); pushdecl (decl); @@ -9434,7 +9531,7 @@ c_simulate_enum_decl (location_t loc, const char *name, unsigned int i; FOR_EACH_VEC_ELT (values, i, value) { - tree decl = build_enumerator (loc, loc, &the_enum, + tree decl = build_enumerator (loc, loc, enumtype, &the_enum, get_identifier (value->first), build_int_cst (integer_type_node, value->second)); diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 8df8f60ef21..40cb5bbbff4 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -3231,7 +3231,7 @@ c_parser_enum_specifier (c_parser *parser) } else enum_value = NULL_TREE; - enum_decl = build_enumerator (decl_loc, value_loc, + enum_decl = build_enumerator (decl_loc, value_loc, type, &the_enum, enum_id, enum_value); if (enum_attrs) decl_attributes (&TREE_PURPOSE (enum_decl), enum_attrs, 0); diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 2bcb9662620..e1cd86cfda7 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -578,7 +578,7 @@ extern int quals_from_declspecs (const struct c_declspecs *); extern struct c_declarator *build_array_declarator (location_t, tree, struct c_declspecs *, bool, bool); -extern tree build_enumerator (location_t, location_t, struct c_enum_contents *, +extern tree build_enumerator (location_t, location_t, tree, struct c_enum_contents *, tree, tree); extern tree check_for_loop_decls (location_t, bool); extern void mark_forward_parm_decls (void); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 4f3611f1b89..5c5fede0bdd 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -1256,11 +1256,16 @@ comptypes_internal (const_tree type1, const_tree type2, bool *enum_and_int_p, case ENUMERAL_TYPE: case RECORD_TYPE: case UNION_TYPE: - if (val != 1 && !same_translation_unit_p (t1, t2)) + if (flag_tag_compat) { tree a1 = TYPE_ATTRIBUTES (t1); tree a2 = TYPE_ATTRIBUTES (t2); + if (ENUMERAL_TYPE != TREE_CODE (t1) + && (TYPE_REVERSE_STORAGE_ORDER (t1) + != TYPE_REVERSE_STORAGE_ORDER (t2))) + return 0; + if (! attribute_list_contained (a1, a2) && ! attribute_list_contained (a2, a1)) break; @@ -1344,41 +1349,6 @@ comp_target_types (location_t location, tree ttl, tree ttr) /* Subroutines of `comptypes'. */ -/* Determine whether two trees derive from the same translation unit. - If the CONTEXT chain ends in a null, that tree's context is still - being parsed, so if two trees have context chains ending in null, - they're in the same translation unit. */ - -bool -same_translation_unit_p (const_tree t1, const_tree t2) -{ - while (t1 && TREE_CODE (t1) != TRANSLATION_UNIT_DECL) - switch (TREE_CODE_CLASS (TREE_CODE (t1))) - { - case tcc_declaration: - t1 = DECL_CONTEXT (t1); break; - case tcc_type: - t1 = TYPE_CONTEXT (t1); break; - case tcc_exceptional: - t1 = BLOCK_SUPERCONTEXT (t1); break; /* assume block */ - default: gcc_unreachable (); - } - - while (t2 && TREE_CODE (t2) != TRANSLATION_UNIT_DECL) - switch (TREE_CODE_CLASS (TREE_CODE (t2))) - { - case tcc_declaration: - t2 = DECL_CONTEXT (t2); break; - case tcc_type: - t2 = TYPE_CONTEXT (t2); break; - case tcc_exceptional: - t2 = BLOCK_SUPERCONTEXT (t2); break; /* assume block */ - default: gcc_unreachable (); - } - - return t1 == t2; -} - /* Allocate the seen two types, assuming that they are compatible. */ static struct tagged_tu_seen_cache * @@ -1457,6 +1427,12 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, if (flag_isoc99 && TYPE_NAME (t1) != TYPE_NAME (t2)) return 0; + if (flag_tag_compat + && (NULL_TREE == TYPE_NAME (t1) + || NULL_TREE == TYPE_NAME (t2) + || TYPE_NAME (t1) != TYPE_NAME (t2))) + return 0; + /* C90 didn't say what happened if one or both of the types were incomplete; we choose to follow C99 rules here, which is that they are compatible. */ @@ -1606,7 +1582,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, return 0; } } - tu->val = needs_warning ? 2 : 10; + tu->val = needs_warning ? 2 : 1; return tu->val; } @@ -1614,6 +1590,12 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2, { struct tagged_tu_seen_cache *tu = alloc_tagged_tu_seen_cache (t1, t2); + if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2))) + { + tu->val = 0; + return 0; + } + for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2); s1 && s2; s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2)) @@ -7046,10 +7028,12 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, /* Aggregates in different TUs might need conversion. */ if ((codel == RECORD_TYPE || codel == UNION_TYPE) - && codel == coder - && comptypes (type, rhstype)) - return convert_and_check (expr_loc != UNKNOWN_LOCATION + && codel == coder) + { + if (comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (rhstype))) + return convert_and_check (expr_loc != UNKNOWN_LOCATION ? expr_loc : location, type, rhs); + } /* Conversion to a transparent union or record from its member types. This applies only to function arguments. */ @@ -8159,6 +8143,15 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype, conversion. */ inside_init = convert (type, inside_init); + if (code == RECORD_TYPE || code == UNION_TYPE) + { + if (!comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (TREE_TYPE (inside_init)))) + { + error_init (init_loc, "invalid initializer %qT %qT", type, TREE_TYPE (inside_init)); + return error_mark_node; + } + } + if (require_constant && TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR) { @@ -10190,7 +10183,7 @@ initialize_elementwise_p (tree type, tree value) return !VECTOR_TYPE_P (value_type); if (AGGREGATE_TYPE_P (type)) - return type != TYPE_MAIN_VARIANT (value_type); + return !comptypes (type, TYPE_MAIN_VARIANT (value_type)); return false; } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d8095e3128f..fdbe625b389 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -2832,6 +2832,11 @@ the target (the default). This option is not supported for C++. @strong{Warning:} the @option{-fsso-struct} switch causes GCC to generate code that is not binary compatible with code generated without it if the specified endianness is not the native endianness of the target. + +@item -ftag-compat +@opindex ftag-compat +This option makes tagged types that are structurally equivalent compatible +and allows identical redeclarations of tagged types in the same scope. @end table @node C++ Dialect Options diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc index f99f7be1c58..dc533fe43f6 100644 --- a/gcc/ipa-free-lang-data.cc +++ b/gcc/ipa-free-lang-data.cc @@ -254,8 +254,9 @@ fld_incomplete_type_of (tree t, class free_lang_data_d *fld) else first = build_reference_type_for_mode (t2, TYPE_MODE (t), TYPE_REF_CAN_ALIAS_ALL (t)); - gcc_assert (TYPE_CANONICAL (t2) != t2 - && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))); + gcc_assert (flag_tag_compat + || (TYPE_CANONICAL (t2) != t2 + && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)))); if (!fld->pset.add (first)) add_tree_to_fld_list (first, fld); return fld_type_variant (first, t, fld); diff --git a/gcc/testsuite/gcc.dg/asan/pr81460.c b/gcc/testsuite/gcc.dg/asan/pr81460.c index 00c1bb7c9f2..98ea40edb56 100644 --- a/gcc/testsuite/gcc.dg/asan/pr81460.c +++ b/gcc/testsuite/gcc.dg/asan/pr81460.c @@ -1,5 +1,6 @@ /* PR sanitizer/80460 */ /* { dg-do compile } */ +/* { dg-options "-fno-tag-compat" } */ int f (int a, struct { int b[a]; } c) /* { dg-warning "anonymous struct declared inside parameter list will not be visible outside of this definition or declaration" } */ diff --git a/gcc/testsuite/gcc.dg/c99-tag-1.c b/gcc/testsuite/gcc.dg/c99-tag-1.c index d7011d2cbec..1b52234ee3f 100644 --- a/gcc/testsuite/gcc.dg/c99-tag-1.c +++ b/gcc/testsuite/gcc.dg/c99-tag-1.c @@ -1,7 +1,7 @@ /* Test for handling of tags (6.7.2.3). */ /* Origin: Joseph Myers <js...@cam.ac.uk> */ /* { dg-do compile } */ -/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */ +/* { dg-options "-std=iso9899:1999 -pedantic-errors -fno-tag-compat" } */ void foo (void) diff --git a/gcc/testsuite/gcc.dg/c99-tag-2.c b/gcc/testsuite/gcc.dg/c99-tag-2.c index 22cf90e27d3..b12c7bcd964 100644 --- a/gcc/testsuite/gcc.dg/c99-tag-2.c +++ b/gcc/testsuite/gcc.dg/c99-tag-2.c @@ -2,7 +2,7 @@ not match one declared in an outer scope. */ /* Origin: Joseph Myers <j...@polyomino.org.uk> */ /* { dg-do compile } */ -/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */ +/* { dg-options "-std=iso9899:1999 -pedantic-errors -fno-tag-compat" } */ struct s; struct t { struct s *p; } x; diff --git a/gcc/testsuite/gcc.dg/decl-3.c b/gcc/testsuite/gcc.dg/decl- 3.c index cba0b906db3..66204270337 100644 --- a/gcc/testsuite/gcc.dg/decl-3.c +++ b/gcc/testsuite/gcc.dg/decl-3.c @@ -1,5 +1,6 @@ /* PR c/9928 */ /* { dg-do compile } */ +/* { dg-options "-fno-tag-compat" } */ enum { CODES }; /* { dg-message "note: previous definition" } */ enum { CODES }; /* { dg-error "conflicting types|redeclaration of enumerator" } */ diff --git a/gcc/testsuite/gcc.dg/enum-redef-1.c b/gcc/testsuite/gcc.dg/enum-redef-1.c index b3fa6cbf8f1..837992f7441 100644 --- a/gcc/testsuite/gcc.dg/enum-redef-1.c +++ b/gcc/testsuite/gcc.dg/enum-redef-1.c @@ -1,3 +1,5 @@ +/* { dg-options "-fno-tag-compat" } */ + enum a { A }; enum a { B }; /* { dg-bogus "nested redefinition" } */ /* { dg-error "redeclaration of 'enum a'" "" { target *-*-* } .-1 } */ diff --git a/gcc/testsuite/gcc.dg/parm-incomplete-1.c b/gcc/testsuite/gcc.dg/parm-incomplete-1.c index 02d97b933f4..fa001d16662 100644 --- a/gcc/testsuite/gcc.dg/parm-incomplete-1.c +++ b/gcc/testsuite/gcc.dg/parm-incomplete-1.c @@ -6,7 +6,7 @@ C99 6.7.5.3); the precise rules are unclear. */ /* Origin: Joseph Myers <j...@polyomino.org.uk> */ /* { dg-do compile } */ -/* { dg-options "" } */ +/* { dg-options "-fno-tag-compat" } */ struct s; void f (struct s); diff --git a/gcc/testsuite/gcc.dg/pr17188-1.c b/gcc/testsuite/gcc.dg/pr17188-1.c index 522a14f7d75..bb31ba30b5d 100644 --- a/gcc/testsuite/gcc.dg/pr17188-1.c +++ b/gcc/testsuite/gcc.dg/pr17188-1.c @@ -3,7 +3,7 @@ diagnosed. Bug 17188. */ /* Origin: Joseph Myers <j...@polyomino.org.uk> */ /* { dg-do compile } */ -/* { dg-options "" } */ +/* { dg-options "-fno-tag-compat" } */ struct s0 { }; /* { dg-message "note: originally defined here" } */ struct s0; diff --git a/gcc/testsuite/gcc.dg/pr18809-1.c b/gcc/testsuite/gcc.dg/pr18809-1.c index 5be41809da6..d7f55feae61 100644 --- a/gcc/testsuite/gcc.dg/pr18809-1.c +++ b/gcc/testsuite/gcc.dg/pr18809-1.c @@ -1,6 +1,7 @@ /* PR c/18809 */ /* Origin: Andrew Pinski <pins...@gcc.gnu.org> */ +/* { dg-options "-pedantic-errors -fno-tag-compat" } */ /* { dg-do compile } */ void foo(enum E e) {} /* { dg-error "forward ref" "forward" } */ diff --git a/gcc/testsuite/gcc.dg/pr27953.c b/gcc/testsuite/gcc.dg/pr27953.c index 99ae0a3aa83..45710f722ab 100644 --- a/gcc/testsuite/gcc.dg/pr27953.c +++ b/gcc/testsuite/gcc.dg/pr27953.c @@ -1,4 +1,5 @@ /* PR c/27953 */ +/* { dg-options "-fno-tag-compat" } */ void foo(struct A a) {} /* { dg-line foo_first } */ /* { dg-warning "declared inside parameter list" "inside" { target *- *-* } .-1 } */ diff --git a/gcc/testsuite/gcc.dg/pr39084.c b/gcc/testsuite/gcc.dg/pr39084.c index ff731492154..776fbea6750 100644 --- a/gcc/testsuite/gcc.dg/pr39084.c +++ b/gcc/testsuite/gcc.dg/pr39084.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -fno-tag-compat" } */ struct color { int i; }; /* { dg-message "note: originally defined here" } */ static const struct color col; diff --git a/gcc/testsuite/gcc.dg/pr68533.c b/gcc/testsuite/gcc.dg/pr68533.c index 49e67a96168..89c03998fdf 100644 --- a/gcc/testsuite/gcc.dg/pr68533.c +++ b/gcc/testsuite/gcc.dg/pr68533.c @@ -1,6 +1,6 @@ /* PR c/68533 */ /* { dg-do compile } */ -/* { dg-options "" } */ +/* { dg-options "-fno-tag-compat" } */ struct T { int t; }; diff --git a/gcc/testsuite/gcc.dg/pr79983.c b/gcc/testsuite/gcc.dg/pr79983.c index 1e292d42108..c9fe72f9169 100644 --- a/gcc/testsuite/gcc.dg/pr79983.c +++ b/gcc/testsuite/gcc.dg/pr79983.c @@ -1,6 +1,6 @@ /* PR c/79983 */ /* { dg-do compile } */ -/* { dg-options "" } */ +/* { dg-options "-fno-tag-compat" } */ struct S; struct S { int i; }; /* { dg-message "originally defined here" } */ diff --git a/gcc/testsuite/gcc.dg/pr89211.c b/gcc/testsuite/gcc.dg/pr89211.c index cf721aa5f62..e2d40aaaf91 100644 --- a/gcc/testsuite/gcc.dg/pr89211.c +++ b/gcc/testsuite/gcc.dg/pr89211.c @@ -1,5 +1,6 @@ /* PR c/89211 */ /* { dg-do compile } */ +/* { dg-options "-fno-tag-compat" } */ void foo (); void foo () diff --git a/gcc/testsuite/gcc.dg/tag-compat.c b/gcc/testsuite/gcc.dg/tag-compat.c new file mode 100644 index 00000000000..54869ea189f --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat.c @@ -0,0 +1,32 @@ +/* + * { dg-do run } + * { dg-options "-ftag-compat" } + */ + +#define product_type(T, A, B) \ +struct product_ ## T { A a ; B b ; } +#define sum_type(T, A, B) \ +struct sum_ ## T { _Bool flag ; union { A a ; B b ; } ; } + +float foo1(product_type(iSfd_, int, sum_type(fd, float, double)) x) +{ + return x.b.a; +} + +static void test1(void) +{ + product_type(iSfd_, int, sum_type(fd, float, double)) y = { 3, { 1, { .a = 1. } } }; + product_type(iSfd_, int, sum_type(fd, float, double)) z = y; + product_type(iSfd_, int, sum_type(fd, float, double)) *zp = &y; + float a = foo1(y); + product_type(iSid_, int, sum_type(id, int, double)) *wp = &y; /* { dg-warning "incompatible pointer type" } */ + float b = foo1(y); + product_type(iSid_, int, sum_type(id, int, double)) w = *wp; + (void)a; (void)b; (void)z; (void)zp; (void)w; (void)wp; +} + +int main() +{ + test1(); +} + diff --git a/gcc/testsuite/gcc.dg/tag-compat10.c b/gcc/testsuite/gcc.dg/tag-compat10.c new file mode 100644 index 00000000000..535793f6f6c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat10.c @@ -0,0 +1,15 @@ +/* + * { dg-do compile } + * { dg-options "-ftag-compat" } + */ + + +extern struct __attribute__(( aligned (16) )) foo { int x; } x; +extern struct bar { float x; } y; + +void test(void) +{ + extern struct __attribute__(( aligned (8) )) foo { int x; } x; /* { dg-error "conflicting types" } */ + extern struct bar { int x; } y; /* { dg-error "conflicting types" } */ +} + diff --git a/gcc/testsuite/gcc.dg/tag-compat11.c b/gcc/testsuite/gcc.dg/tag-compat11.c new file mode 100644 index 00000000000..a83e6efb699 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat11.c @@ -0,0 +1,10 @@ +/* + * { dg-do compile } + * { dg-options "-ftag-compat" } + */ + + +extern struct { int x; } a; +extern struct { int x; } a; /* { dg-error "conflicting types" } */ + + diff --git a/gcc/testsuite/gcc.dg/tag-compat12.c b/gcc/testsuite/gcc.dg/tag-compat12.c new file mode 100644 index 00000000000..f605b24267a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat12.c @@ -0,0 +1,76 @@ +/* + * { dg-do run } + * { dg-options "-ftag-compat -O2" } + */ + + +typedef struct { int x; } foo_t; + +int test_foo(foo_t* a, void* b) +{ + a->x = 1; + + struct { int x; }* p = b; + p->x = 2; + + return a->x; +} + + +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; + p->x = 2; + + return a->x; +} + +int test_bar2(struct bar* a, void* b) +{ + a->x = 1; + + struct bar { int x; int f[0]; }* p = b; + p->x = 2; + + return a->x; +} + +int test_bar3(struct bar* a, void* b) +{ + a->x = 1; + + struct bar { int x; int f[1]; }* p = b; + p->x = 2; + + return a->x; +} + + + +int main() +{ + foo_t y; + + // this works, but is not guaranteed by C + if (2 == test_foo(&y, &y)) + __builtin_abort(); + + struct bar z; + + if (2 != test_bar1(&z, &z)) + __builtin_abort(); + + if (2 != test_bar2(&z, &z)) + __builtin_abort(); + + if (2 == test_bar3(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/tag-compat2.c b/gcc/testsuite/gcc.dg/tag-compat2.c new file mode 100644 index 00000000000..20dc1a9c894 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat2.c @@ -0,0 +1,47 @@ +/* + * { dg-do compile } + * { dg-options "-ftag-compat" } + */ + +typedef struct bar { int x; } X; +typedef struct bar { float x; } Y; /* { dg-warning "redefinition of struct or union" } */ + +void test(void) +{ + struct foo { int x; }; + struct foo { float x; }; /* { dg-warning "redefinition of struct or union" } */ +} + +struct aa { int a; }; + +void f(void) +{ + typedef struct aa A; + struct bb { struct aa a; } x; + struct aa { int a; }; + typedef struct aa A; + struct bb { struct aa a; } y; + (void)x; (void)y; +} + + +union cc { int x; float y; } z; +union cc { int x; float y; } z1; +union cc { float y; int x; } z2; + + + + + +void g(void) +{ + struct s { int a; }; + struct s { int a; } x0; + struct p { struct s c; } y1 = { x0 }; + struct p { struct s { int a; } c; } y = { x0 }; +} + + + + + diff --git a/gcc/testsuite/gcc.dg/tag-compat3.c b/gcc/testsuite/gcc.dg/tag-compat3.c new file mode 100644 index 00000000000..5cbee4c9f38 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat3.c @@ -0,0 +1,49 @@ +/* + * { dg-do compile } + * { dg-options "-ftag-compat" } + */ + + +enum aa { A = 1 } *a; +enum bb { B = 1 } *b; + +void test(void) +{ + enum aa { A = 1 } *c = a; + enum bb { B = 2 } *d = b; /* { dg-warning "incompatible pointer type" } */ +} + +enum cc { C = 1 }; +enum cc { D = 1 }; /* { dg-warning "conflicting redefinition" } */ + +enum dd { E = 1 }; +enum dd { E = 2 }; /* { dg-warning "conflicting redefinition" } */ + /* { dg-error "redeclaration of enumerator" "" { target *-*-* } .-1 } */ + + + +void test2(void) +{ + enum ee *a; + enum ee { F = 2 } *b; + b = a; +} + + +enum ff { G = 2 }; +enum gg { G = 2 }; /* { dg-error "redeclaration of enumerator" } */ +enum g2 { G = 3 }; /* { dg-error "redeclaration of enumerator" } */ + +enum hh { H = 1, H = 1 }; /* { dg-error "redeclaration of enumerator" } */ + +enum ss { K = 2 }; +enum ss { K = 2 }; + +enum tt { R = 2 } TT; +enum tt { + R = _Generic(&TT, enum tt*: 2, default: 0) +}; + + + + diff --git a/gcc/testsuite/gcc.dg/tag-compat4.c b/gcc/testsuite/gcc.dg/tag-compat4.c new file mode 100644 index 00000000000..3d48767d7e0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat4.c @@ -0,0 +1,29 @@ +/* + * { dg-do compile } + * { dg-options "-ftag-compat" } + */ + + +typedef struct p { int a; } pd_t; +typedef struct p { int a; } pd_t; + + +void test1(void) +{ + pd_t y0; + struct p { int a; } x; + y0 = x; +} + +void test2(void) +{ + struct p { int a; } x; + struct p y0 = x; +} + +void test3(void) +{ + struct p { int a; } x; + pd_t y0 = x; +} + diff --git a/gcc/testsuite/gcc.dg/tag-compat5.c b/gcc/testsuite/gcc.dg/tag-compat5.c new file mode 100644 index 00000000000..feeaeb78f57 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat5.c @@ -0,0 +1,15 @@ +/* + * { dg-do compile } + * { dg-options "-ftag-compat" } + */ + + +extern struct foo { int x; } x; +extern struct bar { float x; } y; + +void test(void) +{ + extern struct foo { int x; } x; + extern struct bar { int x; } y; /* { dg-error "conflicting types" } */ +} + diff --git a/gcc/testsuite/gcc.dg/tag-compat6.c b/gcc/testsuite/gcc.dg/tag-compat6.c new file mode 100644 index 00000000000..98ebab7f33d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat6.c @@ -0,0 +1,48 @@ +/* + * { dg-do run } + * { dg-options "-ftag-compat -O2" } + */ + + +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 (A == test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/tag-compat7.c b/gcc/testsuite/gcc.dg/tag-compat7.c new file mode 100644 index 00000000000..e3550fcfee7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat7.c @@ -0,0 +1,73 @@ +/* + * { dg-do run } + * { dg-options "-ftag-compat -O2" } + */ + + +struct foo { int x; }; + +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; +} + +int test_foo2(struct foo* a, void* b) +{ + a->x = 1; + + struct fox { int x; }* p = b; + p->x = 2; + + return a->x; +} + +enum bar { A = 1, B = 3, C = 5, D = 9 }; + +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; +} + +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 (A == test_bar1(&z, &z)) + __builtin_abort(); + + if (A == test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/tag-compat8.c b/gcc/testsuite/gcc.dg/tag-compat8.c new file mode 100644 index 00000000000..210cb39839a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat8.c @@ -0,0 +1,48 @@ +/* + * { dg-do run } + * { dg-options "-ftag-compat -flto -O2" } + */ + + +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 (A == test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/tag-compat9.c b/gcc/testsuite/gcc.dg/tag-compat9.c new file mode 100644 index 00000000000..5bf5dd5fe4f --- /dev/null +++ b/gcc/testsuite/gcc.dg/tag-compat9.c @@ -0,0 +1,73 @@ +/* + * { dg-do run } + * { dg-options "-ftag-compat -flto -O2" } + */ + + +struct foo { int x; }; + +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; +} + +int test_foo2(struct foo* a, void* b) +{ + a->x = 1; + + struct fox { int x; }* p = b; + p->x = 2; + + return a->x; +} + +enum bar { A = 1, B = 3, C = 5, D = 9 }; + +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; +} + +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 (A == test_bar1(&z, &z)) + __builtin_abort(); + + if (A == test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/vla-11.c b/gcc/testsuite/gcc.dg/vla- 11.c index 1504853a55a..982383a980a 100644 --- a/gcc/testsuite/gcc.dg/vla-11.c +++ b/gcc/testsuite/gcc.dg/vla-11.c @@ -4,7 +4,7 @@ these cases). */ /* Origin: Joseph Myers <jos...@codesourcery.com> */ /* { dg-do compile } */ -/* { dg-options "-std=c99 -pedantic-errors" } */ +/* { dg-options "-std=c99 -pedantic-errors -fno-tag-compat" } */ void foo11a(int x[sizeof(int *(*)[*])]); /* { dg-warning "not in a declaration" } */ void foo11b(__SIZE_TYPE__ x, int y[(__UINTPTR_TYPE__)(int (*)[*])x]); /* { dg-warning "not in a declaration" } */ diff --git a/gcc/testsuite/gcc.dg/vla-stexp-2.c b/gcc/testsuite/gcc.dg/vla-stexp-2.c index 9f1512567f0..555c31d2a47 100644 --- a/gcc/testsuite/gcc.dg/vla-stexp-2.c +++ b/gcc/testsuite/gcc.dg/vla-stexp-2.c @@ -1,6 +1,6 @@ /* PR101838 */ /* { dg-do run } */ -/* { dg-options "-Wpedantic -O0" } */ +/* { dg-options "-Wpedantic -O0 -fno-tag-compat" } */ /* { dg-require-effective-target alloca } */