diff options
author | Martin Uecker <uecker@tugraz.at> | 2023-08-15 23:16:35 +0200 |
---|---|---|
committer | Martin Uecker <uecker@tugraz.at> | 2023-12-21 08:43:16 +0100 |
commit | ced651b7757e6ef7e7e309f022cd71b4b6ead295 (patch) | |
tree | ab0b4edd016dcb1f3942777bb3cc25c6f999e553 /gcc/c/c-decl.cc | |
parent | 23fee88f84873b0b8b41c8e5a9b229d533fb4022 (diff) | |
download | gcc-ced651b7757e6ef7e7e309f022cd71b4b6ead295.zip gcc-ced651b7757e6ef7e7e309f022cd71b4b6ead295.tar.gz gcc-ced651b7757e6ef7e7e309f022cd71b4b6ead295.tar.bz2 |
c23: tag compatibility rules for enums
Allow redefinition of enum types and enumerators. Diagnose
nested redefinitions including redefinitions in the enum
specifier for enum types with fixed underlying type.
gcc/c:
* c-tree.h (c_parser_enum_specifier): Add parameter.
* c-decl.cc (start_enum): Allow redefinition.
(finish_enum): Diagnose conflicts.
(build_enumerator): Set context.
(diagnose_mismatched_decls): Diagnose conflicting enumerators.
(push_decl): Preserve context for enumerators.
* c-typeck.cc (tagged_types_tu_compatible_p): Adapt.
* c-parser.cc (c_parser_enum_specifier): Remember when
seen is from an enum type which is not yet defined.
gcc/testsuite:
* gcc.dg/c23-tag-enum-1.c: New test.
* gcc.dg/c23-tag-enum-2.c: New test.
* gcc.dg/c23-tag-enum-3.c: New test.
* gcc.dg/c23-tag-enum-4.c: New test.
* gcc.dg/c23-tag-enum-5.c: New test.
* gcc.dg/gnu23-tag-enum-1.c: Mew test.
Diffstat (limited to 'gcc/c/c-decl.cc')
-rw-r--r-- | gcc/c/c-decl.cc | 65 |
1 files changed, 56 insertions, 9 deletions
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 0e6b4a5..26188aa 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -2112,9 +2112,24 @@ 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_isoc23 + && TYPE_NAME (DECL_CONTEXT (newdecl)) + && DECL_CONTEXT (newdecl) != DECL_CONTEXT (olddecl) + && TYPE_NAME (DECL_CONTEXT (newdecl)) == TYPE_NAME (DECL_CONTEXT (olddecl))) + { + if (!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; } @@ -3275,8 +3290,11 @@ pushdecl (tree x) /* Must set DECL_CONTEXT for everything not at file scope or DECL_FILE_SCOPE_P won't work. Local externs don't count - unless they have initializers (which generate code). */ + unless they have initializers (which generate code). We + also exclude CONST_DECLs because enumerators will get the + type of the enum as context. */ if (current_function_decl + && TREE_CODE (x) != CONST_DECL && (!VAR_OR_FUNCTION_DECL_P (x) || DECL_INITIAL (x) || !TREE_PUBLIC (x))) DECL_CONTEXT (x) = current_function_decl; @@ -9759,7 +9777,7 @@ layout_array_type (tree t) tree start_enum (location_t loc, struct c_enum_contents *the_enum, tree name, - tree fixed_underlying_type) + tree fixed_underlying_type, bool potential_nesting_p) { tree enumtype = NULL_TREE; location_t enumloc = UNKNOWN_LOCATION; @@ -9771,9 +9789,26 @@ 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 (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE) + { + /* If the type is currently being defined or if we have seen an + incomplete version which is now complete, this is a nested + redefinition. The later happens if the redefinition occurs + inside the enum specifier itself. */ + if (C_TYPE_BEING_DEFINED (enumtype) + || (potential_nesting_p && TYPE_VALUES (enumtype) != NULL_TREE)) + error_at (loc, "nested redefinition of %<enum %E%>", name); + + /* For C23 we allow redefinitions. We set to zero and check for + consistency later. */ + if (flag_isoc23 && 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); if (fixed_underlying_type != NULL_TREE) { @@ -9801,9 +9836,6 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name, DECL_SOURCE_LOCATION (TYPE_STUB_DECL (enumtype)) = loc; } - if (C_TYPE_BEING_DEFINED (enumtype)) - error_at (loc, "nested redefinition of %<enum %E%>", name); - C_TYPE_BEING_DEFINED (enumtype) = 1; if (TYPE_VALUES (enumtype) != NULL_TREE) @@ -10033,6 +10065,20 @@ finish_enum (tree enumtype, tree values, tree attributes) && !in_sizeof && !in_typeof && !in_alignof) struct_parse_info->struct_types.safe_push (enumtype); + /* Check for consistency with previous definition */ + if (flag_isoc23) + { + tree vistype = previous_tag (enumtype); + if (vistype + && TREE_CODE (vistype) == TREE_CODE (enumtype) + && !C_TYPE_BEING_DEFINED (vistype)) + { + TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (enumtype); + if (!comptypes_same_p (enumtype, vistype)) + error("conflicting redefinition of enum %qT", enumtype); + } + } + C_TYPE_BEING_DEFINED (enumtype) = 0; return enumtype; @@ -10212,6 +10258,7 @@ build_enumerator (location_t decl_loc, location_t loc, decl = build_decl (decl_loc, CONST_DECL, name, TREE_TYPE (value)); DECL_INITIAL (decl) = value; + DECL_CONTEXT (decl) = the_enum->enum_type; pushdecl (decl); return tree_cons (decl, value, NULL_TREE); @@ -10228,7 +10275,7 @@ c_simulate_enum_decl (location_t loc, const char *name, struct c_enum_contents the_enum; tree enumtype = start_enum (loc, &the_enum, get_identifier (name), - NULL_TREE); + NULL_TREE, false); tree value_chain = NULL_TREE; string_int_pair *value; |