aboutsummaryrefslogtreecommitdiff
path: root/gcc/c
diff options
context:
space:
mode:
authorMartin Uecker <uecker@tugraz.at>2023-08-15 14:58:32 +0200
committerMartin Uecker <uecker@tugraz.at>2023-12-21 08:43:10 +0100
commit23fee88f84873b0b8b41c8e5a9b229d533fb4022 (patch)
tree07d8a1dfd966671d648f7af84c230d231762ab13 /gcc/c
parent514ea1df444ca7f64c3f504ced05d8fb5fbfd62d (diff)
downloadgcc-23fee88f84873b0b8b41c8e5a9b229d533fb4022.zip
gcc-23fee88f84873b0b8b41c8e5a9b229d533fb4022.tar.gz
gcc-23fee88f84873b0b8b41c8e5a9b229d533fb4022.tar.bz2
c23: tag compatibility rules for struct and unions
Implement redeclaration and compatibility rules for structures and unions in C23. gcc/c/: * c-decl.cc (previous_tag): New function. (parser_xref_tag): Find earlier definition. (get_parm_info): Turn off warning for C23. (start_struct): Allow redefinitons. (finish_struct): Diagnose conflicts. * c-tree.h (comptypes_same_p): Add prototype. * c-typeck.cc (comptypes_same_p): New function. (comptypes_internal): Activate comparison of tagged types. (convert_for_assignment): Ignore qualifiers. (digest_init): Add error. (initialized_elementwise_p): Allow compatible types. gcc/testsuite/: * gcc.dg/c23-enum-7.c: Remove warning. * gcc.dg/c23-tag-1.c: New test. * gcc.dg/c23-tag-2.c: New deactivated test. * gcc.dg/c23-tag-3.c: New test. * gcc.dg/c23-tag-4.c: New test. * gcc.dg/c23-tag-5.c: New deactivated test. * gcc.dg/c23-tag-6.c: New test. * gcc.dg/c23-tag-7.c: New test. * gcc.dg/c23-tag-8.c: New test. * gcc.dg/gnu23-tag-1.c: New test. * gcc.dg/gnu23-tag-2.c: New test. * gcc.dg/gnu23-tag-3.c: New test. * gcc.dg/gnu23-tag-4.c: New test. * gcc.dg/pr112488-2.c: Remove warning.
Diffstat (limited to 'gcc/c')
-rw-r--r--gcc/c/c-decl.cc72
-rw-r--r--gcc/c/c-tree.h1
-rw-r--r--gcc/c/c-typeck.cc38
3 files changed, 99 insertions, 12 deletions
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 039a66f..0e6b4a5 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2037,6 +2037,28 @@ locate_old_decl (tree decl)
decl, TREE_TYPE (decl));
}
+
+/* Helper function. For a tagged type, it finds the declaration
+ for a visible tag declared in the the same scope if such a
+ declaration exists. */
+static tree
+previous_tag (tree type)
+{
+ struct c_binding *b = NULL;
+ tree name = TYPE_NAME (type);
+
+ if (name)
+ b = I_TAG_BINDING (name);
+
+ if (b)
+ b = b->shadowed;
+
+ if (b && B_IN_CURRENT_SCOPE (b))
+ return b->decl;
+
+ return NULL_TREE;
+}
+
/* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL.
Returns true if the caller should proceed to merge the two, false
if OLDDECL should simply be discarded. As a side effect, issues
@@ -8573,11 +8595,14 @@ get_parm_info (bool ellipsis, tree expr)
if (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE)
{
if (b->id)
- /* The %s will be one of 'struct', 'union', or 'enum'. */
- warning_at (b->locus, 0,
- "%<%s %E%> declared inside parameter list"
- " will not be visible outside of this definition or"
- " declaration", keyword, b->id);
+ {
+ /* The %s will be one of 'struct', 'union', or 'enum'. */
+ if (!flag_isoc23)
+ warning_at (b->locus, 0,
+ "%<%s %E%> declared inside parameter list"
+ " will not be visible outside of this definition or"
+ " declaration", keyword, b->id);
+ }
else
/* The %s will be one of 'struct', 'union', or 'enum'. */
warning_at (b->locus, 0,
@@ -8668,6 +8693,16 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
present, only a definition in the current scope is relevant. */
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). */
+ if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref))
+ {
+ tree vis = previous_tag (ref);
+ if (vis)
+ ref = vis;
+ }
+
/* If this is the right type of tag, return what we found.
(This reference will be shadowed by shadow_tag later if appropriate.)
If this is the wrong type of tag, do not return it. If it was the
@@ -8782,6 +8817,14 @@ start_struct (location_t loc, enum tree_code code, tree name,
if (name != NULL_TREE)
ref = lookup_tag (code, name, true, &refloc);
+
+ /* For C23, even if we already have a completed definition,
+ we do not use it. We will check for consistency later.
+ If we are in a nested redefinition the type is not
+ complete. We will then detect this below. */
+ if (flag_isoc23 && ref && TYPE_SIZE (ref))
+ ref = NULL_TREE;
+
if (ref && TREE_CODE (ref) == code)
{
if (TYPE_STUB_DECL (ref))
@@ -9581,6 +9624,25 @@ 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_isoc23)
+ {
+ tree vistype = previous_tag (t);
+ if (vistype
+ && TREE_CODE (vistype) == TREE_CODE (t)
+ && !C_TYPE_BEING_DEFINED (vistype))
+ {
+ TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (t);
+ if (c_type_variably_modified_p (t))
+ error ("redefinition of struct or union %qT with variably "
+ "modified type", t);
+ else if (!comptypes_same_p (t, vistype))
+ error ("redefinition of struct or union %qT", t);
+ }
+ }
+
+ C_TYPE_BEING_DEFINED (t) = 0;
+
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-tree.h b/gcc/c/c-tree.h
index b325723..e53948f 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -757,6 +757,7 @@ extern tree c_objc_common_truthvalue_conversion (location_t, tree);
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 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 a2164d9..9dfc9f6 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1080,6 +1080,23 @@ comptypes (tree type1, tree type2)
return ret ? (data.warning_needed ? 2 : 1) : 0;
}
+
+/* Like comptypes, but it returns non-zero only for identical
+ types. */
+
+bool
+comptypes_same_p (tree type1, tree type2)
+{
+ struct comptypes_data data = { };
+ bool ret = comptypes_internal (type1, type2, &data);
+
+ if (data.different_types_p)
+ return false;
+
+ return ret;
+}
+
+
/* Like comptypes, but if it returns non-zero because enum and int are
compatible, it sets *ENUM_AND_INT_P to true. */
@@ -1266,11 +1283,11 @@ comptypes_internal (const_tree type1, const_tree type2,
case ENUMERAL_TYPE:
case RECORD_TYPE:
case UNION_TYPE:
- if (false)
- {
- return tagged_types_tu_compatible_p (t1, t2, data);
- }
- return false;
+
+ if (!flag_isoc23)
+ return false;
+
+ return tagged_types_tu_compatible_p (t1, t2, data);
case VECTOR_TYPE:
return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
@@ -7102,7 +7119,7 @@ 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))
+ && comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (rhstype)))
return convert_and_check (expr_loc != UNKNOWN_LOCATION
? expr_loc : location, type, rhs);
@@ -8457,6 +8474,13 @@ 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)
+ && !comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (TREE_TYPE (inside_init))))
+ {
+ error_init (init_loc, "invalid initializer");
+ return error_mark_node;
+ }
+
if (require_constant
&& TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR)
{
@@ -10542,7 +10566,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;
}