This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)


The attached patch implements three new warnings:

 *  -Wstruct-not-pod triggers for struct definitions that are not
    POD structs,
 *  -Wclass-is-pod triggers for class definitions that satisfy
    the requirements on POD structs, and
 *  -Wmismatched-tags that triggers for class and struct declarations
    with class-key that doesn't match either their definition or
    the first declaration (if no definition is provided).

The implementation of -Wclass-is-pod and -Wstruct-not-pod is fairly
straightforward but the -Wmismatched-tags solution is slightly unusual.
It collects struct and class declarations first and diagnoses mismatches
only after the whole tramslation unit has been processed.  This is so
that the definition of a class can guide which declarations to diagnose
no matter which come first.

Martin
gcc/c-family/ChangeLog:

	* c.opt (-Wstruct-not-pod, -Wclass-is-pod): New options.
	(-Wmismatched-tags): Same.

gcc/cp/ChangeLog:

	* parser.c (maybe_warn_struct_vs_class): New function.
	(cp_parser_check_class_key): Add argument.
	(cp_parser_type_specifier): Call maybe_warn_struct_vs_class.
	(cp_parser_elaborated_type_specifier): Call maybe_warn_struct_vs_class
	before setting CLASSTYPE_DECLARED_CLASS.  Avoid setting it for classes
	that are in the process of being defined.
	(cp_parser_class_head): Call maybe_warn_struct_vs_class.
	(class_or_template_pod_p): New static function.
	(maybe_warn_struct_vs_class) Same.
	(class rec_decl_loc_t): New.
	(cp_parser_check_class_key): Record a struct declaration.
	(diag_mismatched_tags): Hanlde -Wmismatched-tags.
	(c_parse_file): Call diag_mismatched_tags.

gcc/ChangeLog:

	* doc/invoke.texi (-Wstruct-not-pod, -Wclass-is-pod): Document new
	options.
	(-Wmismatched-tags): Same.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 080066fa608..0bc23a6f409 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -736,6 +736,10 @@ Wmisleading-indentation
 C C++ Common Var(warn_misleading_indentation) Warning LangEnabledBy(C C++,Wall)
 Warn when the indentation of the code does not reflect the block structure.
 
+Wmismatched-tags
+C++ Objc++ Var(warn_mismatched_tags) Warning
+Warn when a class is redeclared using a mismatched class-key.
+
 Wmissing-braces
 C ObjC C++ ObjC++ Var(warn_missing_braces) Warning LangEnabledBy(C ObjC,Wall)
 Warn about possibly missing braces around initializers.
@@ -794,6 +798,14 @@ Wstringop-truncation
 C ObjC C++ LTO ObjC++ Var(warn_stringop_truncation) Warning Init (1) LangEnabledBy(C ObjC C++ LTO ObjC++, Wall)
 Warn about truncation in string manipulation functions like strncat and strncpy.
 
+Wstruct-not-pod
+C++ ObjC++ Var(warn_struct_not_pod) Warning
+Warn about structs that are not POD.
+
+Wclass-is-pod
+C++ ObjC++ Var(warn_class_is_pod) Warning
+Warn about classes that are POD.
+
 Wsuggest-attribute=format
 C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
 Warn about functions which might be candidates for format attributes.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 12814102465..74accff2c6d 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -262,6 +262,8 @@ static bool cp_parser_omp_declare_reduction_exprs
 static void cp_finalize_oacc_routine
   (cp_parser *, tree, bool);
 
+static void maybe_warn_struct_vs_class (location_t, tree);
+
 /* Manifest constants.  */
 #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token))
 #define CP_SAVED_TOKEN_STACK 5
@@ -2580,7 +2582,7 @@ static enum tag_types cp_parser_token_is_class_key
 static enum tag_types cp_parser_token_is_type_parameter_key
   (cp_token *);
 static void cp_parser_check_class_key
-  (enum tag_types, tree type);
+(cp_parser *, enum tag_types, tree type, bool);
 static void cp_parser_check_access_in_redeclaration
   (tree type, location_t location);
 static bool cp_parser_optional_template_keyword
@@ -17442,6 +17444,8 @@ cp_parser_type_specifier (cp_parser* parser,
 					  type_spec,
 					  token,
 					  /*type_definition_p=*/true);
+
+	  maybe_warn_struct_vs_class (token->location, type_spec);
 	  return type_spec;
 	}
 
@@ -18621,11 +18625,13 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 
   if (tag_type != enum_type)
     {
+      /* Diagnose class/struct/union mismatches.  */
+      cp_parser_check_class_key (parser, tag_type, type, false);
+
       /* Indicate whether this class was declared as a `class' or as a
 	 `struct'.  */
-      if (CLASS_TYPE_P (type))
+      if (CLASS_TYPE_P (type) && !currently_open_class (type))
 	CLASSTYPE_DECLARED_CLASS (type) = (tag_type == class_type);
-      cp_parser_check_class_key (tag_type, type);
     }
 
   /* A "<" cannot follow an elaborated type specifier.  If that
@@ -24168,11 +24174,13 @@ cp_parser_class_head (cp_parser* parser,
 		       parser->num_template_parameter_lists);
     }
 
+  /* Diagnose class/struct/union mismatches.  */
+  cp_parser_check_class_key (parser, class_key, type, true);
+
   /* Indicate whether this class was declared as a `class' or as a
      `struct'.  */
   if (TREE_CODE (type) == RECORD_TYPE)
-    CLASSTYPE_DECLARED_CLASS (type) = (class_key == class_type);
-  cp_parser_check_class_key (class_key, type);
+    CLASSTYPE_DECLARED_CLASS (type) = class_key == class_type;
 
   /* If this type was already complete, and we see another definition,
      that's an error.  Likewise if the type is already being defined:
@@ -28039,6 +28047,127 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
   return fn;
 }
 
+/* Return true if the class or class template TYPE appears to meet
+   the requirements of a POD type even if some of the instantiations
+   of the latter may not.  The result is not exactly the same as
+   that of a call to pod_type_p.  */
+
+static bool
+class_or_template_pod_p (tree type)
+{
+  if (type == error_mark_node
+      || TYPE_HAS_USER_CONSTRUCTOR (type)
+      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
+      || (TYPE_LANG_SPECIFIC (type)
+	  && TYPE_HAS_COPY_ASSIGN (type)
+	  && (cxx_dialect != cxx98
+	      || (TYPE_LANG_SPECIFIC (type)
+		  && !TYPE_HAS_TRIVIAL_COPY_ASSIGN (type)))))
+    return false;
+
+  /* Stores the last access specifier.  */
+  tree last_access = NULL_TREE;
+
+  for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+    {
+      if (DECL_ARTIFICIAL (fld))
+	continue;
+
+      if (TREE_CODE (fld) == FIELD_DECL
+	  && !TREE_STATIC (fld)
+	  && TREE_TYPE (fld))
+	{
+	  tree fldtype = TREE_TYPE (fld);
+	  if (fldtype == error_mark_node)
+	    return false;
+
+	  if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (fldtype))
+	    continue;
+
+	  if (TREE_CODE (fldtype) == REFERENCE_TYPE)
+	    return false;
+
+	  tree access = declared_access (fld);
+	  if (last_access && access != last_access)
+	    return false;
+	  last_access = access;
+
+	  if (TREE_CODE (fldtype) == RECORD_TYPE
+	      && !class_or_template_pod_p (fldtype))
+	    return false;
+	}
+      else if (TREE_CODE (fld) == FUNCTION_DECL
+	  && DECL_NONSTATIC_MEMBER_FUNCTION_P (fld)
+	  && DECL_VIRTUAL_P (fld))
+	return false;
+    }
+
+  return true;
+}
+
+/* For a DECL of class type, issue a warning when it is a POD type
+   and is declared with the class-key class, or when it is not a POD
+   type and is declared withe the class-key struct.  When DECL refers
+   to a class template, consider instead whether it has a ctor, dtor,
+   or copy assignment operator as a proxy.  */
+
+static void
+maybe_warn_struct_vs_class (location_t loc, tree type)
+{
+  if (TREE_CODE (type) != RECORD_TYPE)
+    return;
+
+  bool warned = false;
+  const char *key = class_key_or_enum_as_string (type);
+  if (processing_template_decl)
+    {
+      if (class_or_template_pod_p (type))
+	{
+	  if (!strcmp (key, "class"))
+	    warned = warning_at (loc, OPT_Wclass_is_pod,
+				 "POD-like template %qT declared with "
+				 "class-key %qs; use %qs instead",
+				 type, key, "struct");
+	}
+      else if (strcmp (key, "class"))
+	warned = warning_at (loc, OPT_Wstruct_not_pod,
+			     "non-POD-like template %qT declared with "
+			     "class-key %qs; use %qs instead",
+			     type, key, "class");
+    }
+  else
+    {
+      if (pod_type_p (type))
+	{
+	  if (!strcmp (key, "class"))
+	    warned = warning_at (loc, OPT_Wclass_is_pod,
+				 "POD type %qT declared with class-key %qs; "
+				 "use %qs instead",
+				 type, key, "struct");
+	}
+      /* In C++ 98 mode call class_or_template_pod_p to get the same
+	 result as in later modes.  There is little point in enforcing
+	 consistency with the more restrictive rules.  */
+      else if (cxx_dialect == cxx98 && class_or_template_pod_p (type))
+	{
+	  if (!strcmp (key, "class"))
+	    warned = warning_at (loc, OPT_Wclass_is_pod,
+				 "C++11 POD type %qT declared with "
+				 "class-key %qs; use %qs instead",
+				 type, key, "struct");
+	}
+      else if (strcmp (key, "class"))
+	warned = warning_at (loc, OPT_Wstruct_not_pod,
+			     "non-POD type %qT declared with class-key %qs; "
+			     "use %qs instead",
+			     type, key, "class");
+    }
+
+  if (warned)
+    inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)),
+	    "previous declaration here");
+}
+
 /* Parse a template-declaration body (following argument list).  */
 
 static void
@@ -29915,14 +30044,107 @@ cp_parser_token_is_type_parameter_key (cp_token* token)
     }
 }
 
-/* Issue an error message if the CLASS_KEY does not match the TYPE.  */
+/* Describes the set of declarations of a struct, class, or class template
+   or its specializations.  Used for -Wmismatched-tags.  */
+
+class rec_decl_loc_t
+{
+ public:
+
+  rec_decl_loc_t ()
+    : locvec (), idxdef (), def_class_key ()
+  {
+    locvec.create (4);
+  }
+
+  /* Construct an object for a single declaration of a class with
+     CLASS_KEY at the current location in the current function (or
+     at another scope).  KEY_REDUNDANT is true if the class-key may
+     be omitted in the current context without a change in semantics.
+     DEF_P is true for a declaration that is a definition.  */
+  rec_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p)
+    : locvec (), idxdef (def_p ? 0 : UINT_MAX),
+    def_class_key (class_key)
+  {
+    locvec.create (4);
+    func_loc_t func_loc (current_function_decl, input_location);
+    flag_func_loc_t flag_func_loc (key_redundant, func_loc);
+    locvec.quick_push (class_key_loc_t (class_key, flag_func_loc));
+  }
+
+  /* Copy, assign, and destroy the object.  Necessary because LOCVEC
+     isn't safely copyable and assignable and doesn't release storage
+     on its own.  */
+  rec_decl_loc_t (const rec_decl_loc_t &rhs)
+    : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef),
+      def_class_key (rhs.def_class_key) { }
+
+  rec_decl_loc_t& operator= (const rec_decl_loc_t &rhs)
+  {
+    locvec.release ();
+    locvec = rhs.locvec.copy ();
+    idxdef = rhs.idxdef;
+    def_class_key = rhs.def_class_key;
+    return *this;
+  }
+
+  ~rec_decl_loc_t ()
+  {
+    locvec.release ();
+  }
+
+  tree function (unsigned i) const
+  {
+    return locvec[i].second.second.first;
+  }
+
+  location_t location (unsigned i) const
+  {
+    return locvec[i].second.second.second;
+  }
+
+  bool key_redundant (unsigned i) const
+  {
+    return locvec[i].second.first;
+  }
+
+  tag_types class_key (unsigned i) const
+  {
+    return locvec[i].first;
+  }
+
+  /* The locations of local variables/alloca calls returned by the return
+     statement.  Avoid using auto_vec here since it's not safe to copy due
+     to pr90904.  */
+  typedef std::pair<tree, location_t>           func_loc_t;
+  typedef std::pair<bool, func_loc_t>           flag_func_loc_t;
+  typedef std::pair<tag_types, flag_func_loc_t> class_key_loc_t;
+  vec <class_key_loc_t> locvec;
+  /* LOCVEC index of the definition or -1 if none exists.  */
+  unsigned idxdef;
+  /* The class-key the class was last declared with or none_type when
+     it has been declared with a mismatched key.  */
+  tag_types def_class_key;
+};
+
+
+/* A mapping between a TYPE_DECL and the rec_decl_loc_t description
+   above.  */
+typedef hash_map<tree, rec_decl_loc_t> record_to_locs_t;
+static record_to_locs_t rec2loc;
+
+/* Issue an error message if the CLASS_KEY does not match the TYPE.
+   DEF_P is expected to be set for a definition.  */
 
 static void
-cp_parser_check_class_key (enum tag_types class_key, tree type)
+cp_parser_check_class_key (cp_parser *parser, enum tag_types class_key,
+			   tree type, bool def_p)
 {
   if (type == error_mark_node)
     return;
-  if ((TREE_CODE (type) == UNION_TYPE) != (class_key == union_type))
+
+  bool seen_as_union = TREE_CODE (type) == UNION_TYPE;
+  if (seen_as_union != (class_key == union_type))
     {
       if (permerror (input_location, "%qs tag used in naming %q#T",
 		     class_key == union_type ? "union"
@@ -29930,7 +30152,132 @@ cp_parser_check_class_key (enum tag_types class_key, tree type)
 		     type))
 	inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)),
 		"%q#T was previously declared here", type);
+      return;
+    }
+
+  if (seen_as_union || !warn_mismatched_tags)
+    return;
+
+  tree type_decl = TYPE_MAIN_DECL (type);
+  tree name = DECL_NAME (type_decl);
+  /* Look up the NAME to see if it unambiguously refers to the TYPE
+     and set KEY_REDUNDANT if so.  */
+  tree decl = cp_parser_lookup_name_simple (parser, name, input_location);
+  bool key_redundant = decl == type_decl;
+
+  if (rec_decl_loc_t *rdl = rec2loc.get (type_decl))
+    {
+      /* A declatation for this TYPE has already been seen earlier.
+	 Reset the class_key associated with this type on mismatch.
+	 This is an optimization that lets the diagnostic code skip
+	 over classes that use the same class-key in all declarations.  */
+      if (rdl->def_class_key != class_key)
+	rdl->def_class_key = none_type;
+
+      /* Set IDXDEF to the index of the vector corresponding to
+	 the definition.  */
+      if (def_p)
+	rdl->idxdef = rdl->locvec.length ();
+      typedef rec_decl_loc_t::func_loc_t      func_loc_t;
+      typedef rec_decl_loc_t::flag_func_loc_t flag_func_loc_t;
+      typedef rec_decl_loc_t::class_key_loc_t class_key_loc_t;
+
+      /* Append a record of this declaration to the vector.  */
+      rdl->locvec.safe_push (class_key_loc_t (class_key,
+        flag_func_loc_t (key_redundant,
+			 func_loc_t (current_function_decl, input_location))));
     }
+  else
+    /* Create a new entry for this (new) declaration.  */
+    rec2loc.put (type_decl, rec_decl_loc_t (class_key, key_redundant, def_p));
+}
+
+/* Issue -Wmismatched-tags.  */
+
+static void
+diag_mismatched_tags ()
+{
+  /* REC2LOC should be empty if -Wmismatched-tags is disabled.  */
+  gcc_assert (warn_mismatched_tags || rec2loc.is_empty ());
+
+  /* Save the current function before changing it below.  It should
+     be null at this point.  */
+  tree save_func = current_function_decl;
+
+  /* Iterate over the collected class/struct declarations.  */
+  typedef record_to_locs_t::iterator iter_t;
+  for (iter_t it = rec2loc.begin (); it != rec2loc.end (); ++it)
+    {
+      const rec_decl_loc_t &recloc = (*it).second;
+      unsigned ndecls = recloc.locvec.length ();
+
+      /* Skip those that consistently use the same class-key. */
+      if (recloc.def_class_key != none_type || ndecls < 2)
+	continue;
+
+      tree type_decl = (*it).first;
+      bool def_p = recloc.idxdef < ndecls;
+      unsigned idxguide = def_p ? recloc.idxdef : 0;
+      unsigned idx = 0;
+      /* Advance IDX to the first declaration that either is not
+	 a definition or that doesn't match the first declaration
+         if no definition is provided.  */
+      while (recloc.class_key (idx) == recloc.class_key (idxguide))
+	++idx;
+
+      /* The class-key the class is expected to be declared with: it's
+	 either the key used in its definition or the first declaration
+	 if no definition has been provided.  */
+      tag_types expect_class_key = recloc.class_key (def_p ? idxguide : 0);
+      const char *keystr = expect_class_key == record_type ? "struct" : "class";
+      /* Set the function declaration to print in diagnostic context.  */
+      current_function_decl = recloc.function (idx);
+      auto_diagnostic_group d;
+      /* Issue a warning for the first mismatched declaration.
+	 Avoid using "%#qT" since the class-key for the same type will
+	 be the same regardless of which one was used in the declaraion.  */
+      warning_at (recloc.location (idx), OPT_Wmismatched_tags,
+		  "%<%s %T%> declared with a mismatched class-key",
+		  keystr, type_decl);
+
+      /* Mention the first declaration or definition that guided
+	 the decision to issue the warning above.  */
+      tag_types class_key = recloc.class_key (idxguide);
+      inform (recloc.location (idxguide),
+              (def_p
+               ? G_("%qT defined as %qs here")
+               : G_("%qT first declared as %qs here")),
+               type_decl, class_key == record_type ? "struct" : "class");
+
+      /* Issue warnings for the remaining insistent declarations.  */
+      for (unsigned i = idx + 1; i != ndecls; ++i)
+	{
+	  class_key = recloc.class_key (i);
+	  /* Skip over the declarations that match either the definition
+	     if one was provided or the first declaration.  */
+	  if (class_key == expect_class_key)
+	    continue;
+
+	  location_t loc = recloc.location (i);
+	  bool key_redundant = recloc.key_redundant (i);
+          /* Set the function declaration to print in diagnostic context.  */
+          current_function_decl = recloc.function (i);
+	  warning_at (loc, OPT_Wmismatched_tags,
+		      "%<%s %T%> declared with a mismatched class-key",
+		      keystr, type_decl);
+	  /* Suggest how to avoid the warning for each instance since
+	     the guidance may be different depending on context.  */
+	  inform (loc,
+		  (key_redundant
+		   ? G_("remove the class-key or replace it with %qs")
+		   : G_("replace the class-key with %qs")),
+		  keystr);
+	}
+    }
+
+  rec2loc.empty ();
+  /* Restore the current function.  */
+  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
@@ -41493,6 +41840,8 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+  diag_mismatched_tags ();
+
   the_parser = NULL;
 
   finish_translation_unit ();
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9c6050b574b..6e8563401fc 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -230,15 +230,17 @@ in the following sections.
 -fvisibility-inlines-hidden @gol
 -fvisibility-ms-compat @gol
 -fext-numeric-literals @gol
--Wabi=@var{n}  -Wabi-tag  -Wconversion-null  -Wctor-dtor-privacy @gol
+-Wabi=@var{n}  -Wabi-tag  -Wclass-is-pod @gol
+-Wconversion-null  -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wdeprecated-copy  -Wdeprecated-copy-dtor @gol
 -Wliteral-suffix @gol
+-Wmismatched-tags @gol
 -Wmultiple-inheritance  -Wno-init-list-lifetime @gol
 -Wnamespaces  -Wnarrowing @gol
 -Wpessimizing-move  -Wredundant-move @gol
 -Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
 -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
--Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
+-Weffc++  -Wstrict-null-sentinel  -Wstruct-is-pod  -Wtemplates @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
 -Wno-class-conversion  -Wno-terminate @gol
@@ -3191,6 +3193,28 @@ void h() @{ f(g); @}
 In C++14, @code{f} calls @code{f<void(*)()>}, but in
 C++17 it calls @code{f<void(*)()noexcept>}.
 
+@item -Wclass-is-pod @r{(C++ and Objective-C++ only)}
+@opindex Wclass-is-pod
+@opindex Wno-class-is-pod
+Warn for definitions of classes and class templates with the class-key
+@code{class} that satisfy the C++ requirements for a POD (Plain Old Data)
+struct.  A class template definition is considered to satisfy the requirements
+if its instantiation on a POD type does, even if its instantiation on a type
+that does not meet such requirements would not.  For example, template
+@code{Pair} below triggers the warning because it satisfies the requirements
+of a POD struct for any POD @code{T} and @code{U}.  Defining the template
+with the class-key @code{struct} avoids the warning.
+@smallexample
+template <class T, class U>
+class Pair @{
+public:
+  T a;
+  T b;
+@}
+@end smallexample
+For best results use the @option{-Wclass-is-pod} option in conjunction
+with options @option{-Wstruct-not-pod} and @option{-Wmismatched-tags}.
+
 @item -Wclass-memaccess @r{(C++ and Objective-C++ only)}
 @opindex Wclass-memaccess
 @opindex Wno-class-memaccess
@@ -3384,6 +3408,28 @@ to @code{__null}.  Although it is a null pointer constant rather than a
 null pointer, it is guaranteed to be of the same size as a pointer.
 But this use is not portable across different compilers.
 
+@item -Wstruct-not-pod @r{(C++ and Objective-C++ only)}
+@opindex Wstruct-not-pod
+@opindex Wno-struct-pod
+Warn for definitions of structs and class templates using the class-key
+@code{struct} that do not satisfy the C++ requirements for a POD (Plain Old
+Data) struct.  A class template definition is considered to satisfy
+the requirements if and only if its instantiation on a POD type does.  For
+example, template @code{Pair} below triggers the warning because it does
+not satisfy the requirements of a POD struct for any POD @code{T} and @code{U}.
+Defining the template with the class-key @code{class} avoids the warning, as
+does removing the default constructor.
+@smallexample
+template <class T, class U>
+struct Pair @{
+  Pair (): a (), b () @{ @}
+  T a;
+  T b;
+@}
+@end smallexample
+For best results use the @option{-Wstruct-not-pod} option in conjunction
+with options @option{-Wclass-is-pod} and @option{-Wmismatched-tags}.
+
 @item -Wno-non-template-friend @r{(C++ and Objective-C++ only)}
 @opindex Wno-non-template-friend
 @opindex Wnon-template-friend
@@ -3455,6 +3501,30 @@ The warning is inactive inside a system header file, such as the STL, so
 one can still use the STL.  One may also instantiate or specialize
 templates.
 
+@item -Wmismatched-tags @r{(C++ and Objective-C++ only)}
+@opindex Wmismatched-tags
+@opindex Wno-mismatched-tags
+Warn for declarations of structs, classes, and class templates and their
+specializations with a class-key that does not match either the definition
+or the first declaration if no definition is provided.
+
+For example, the declaration of @code{struct Object} in the argument list
+of @code{draw} triggers the warning.  To avoid it, either remove the redundant
+class-key @code{struct} or replace it with @code{class} to match its definition.
+@smallexample
+class Object @{
+public:
+  virtual ~Object () = 0;
+@};
+void draw (struct Object*);
+@end smallexample
+
+It is not wrong to declare a class with the class-key @code{struct} as
+the example above shows.  The @option{-Wmismatched-tags} option is intended
+to help achieve a consistent style of class declarations.  It can be used
+either on its own or in conjunction with options @option{-Wclass-is-pod}
+and @option{-Wstruct-not-pod}.
+
 @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)}
 @opindex Wmultiple-inheritance
 @opindex Wno-multiple-inheritance
diff --git a/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C b/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C
new file mode 100644
index 00000000000..efe0889e429
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C
@@ -0,0 +1,127 @@
+// { dg-do compile }
+// { dg-options "-Wclass-is-pod" }
+
+namespace Pod
+{
+class A                 // { dg-warning "POD type 'Pod::A' declared with class-key 'class'; use 'struct' instead" }
+{ };
+class B                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ public: int i; };
+class C                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ public: void f (); };
+class D                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ void operator= (int); };
+
+#if __cplusplus > 199711L
+class E                 // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } }
+  : A { };
+class F                 // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } }
+  : E { };
+class G                 // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } }
+  : private A { };
+#endif
+}
+
+
+namespace PodTemplate
+{
+template <class>
+class A                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ };
+template <class>
+class B                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ public: int i; };
+template <class>
+class C                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ public: void f (); };
+template <class>
+class D                 // { dg-warning "\\\[-Wclass-is-pod" }
+{ void operator= (int); };
+
+#if __cplusplus > 199711L
+template <class T>
+class E                 // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } }
+  : A<T> { };
+template <class T>
+class F                 // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } }
+  : E<T> { };
+template <class T>
+class G                 // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } }
+  : private A<T> { };
+#endif
+}
+
+
+namespace NonPodDueToSpecialFunctions
+{
+class A
+{ public: A (); };
+class B
+{ public: B (int); };
+
+class C
+{ public: C (C&); };
+
+class D
+{ public: ~D (); };
+
+class E
+{ public: void operator= (E&); };
+}
+
+
+namespace NonPodDueToVirtuals
+{
+class A
+{ public: virtual void f (); };
+
+}
+
+
+namespace NonPodDueToNonPodMembers
+{
+class A
+{ public: int &r; };
+
+class B { public: B (); };
+
+class C
+{ public: B b; };
+}
+
+
+namespace NonPodTemplateDueToNonPodMembers
+{
+template <class T>
+class A
+{ public: T &r; };
+
+class B { public: B (); };
+
+template <class>
+class C
+{ public: B b; };
+}
+
+
+
+namespace NonPodDueToAccess
+{
+class A
+{ int i; public: int j; };
+
+class B
+{ int i; protected: int j; };
+}
+
+
+namespace NonPodDueToBases
+{
+struct A { };
+struct B { };
+class C: A, B           // { dg-bogus "\\\[-Wclass-is-pod" "pr91064" { xfail *-*-* } }
+{ };
+
+class D: virtual A
+{ };
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
new file mode 100644
index 00000000000..1166299ef67
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
@@ -0,0 +1,217 @@
+// Test to verify that -Wmismatched-tags is issued for declarations
+// of the same class using different class-ids.
+// { dg-do compile }
+// { dg-options "-Wmismatched-tags -ftrack-macro-expansion=0" }
+
+namespace Classes
+{
+class A;
+class A;
+
+struct B;
+struct B;
+
+union C;
+union C;
+
+struct D;                   // { dg-warning "'class Classes::D' declared with a mismatched class-key" }
+class D                     // { dg-message "'Classes::D' defined as 'class' here" }
+{ public: D (); };
+
+class E;                    // { dg-warning "'struct Classes::E' declared with a mismatched class-key" }
+struct E                    // { dg-message "'Classes::E' defined as 'struct' here" }
+{ int i; };
+
+class D;
+struct E;
+
+class D;
+struct E;
+
+struct D;                   // { dg-warning ".class Classes::D. declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint" { target *-*-* } .-1 }
+
+class E;                    // { dg-warning "'struct Classes::E' declared with a mismatched class-key" }
+}
+
+
+namespace GlobalObjects
+{
+class A;                    // { dg-message "'GlobalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'GlobalObjects::B' first declared as 'struct' here" }
+
+extern class A a1;
+extern class A a2;
+
+extern struct B b1;
+extern struct B b2;
+
+extern struct A a3;         // { dg-warning ".class GlobalObjects::A. declared with a mismatched class-key" }
+extern class A a4;
+
+extern class B b3;         // { dg-warning ".struct GlobalObjects::B. declared with a mismatched class-key" }
+extern struct B b4;
+}
+
+
+namespace LocalObjects
+{
+class A;                    // { dg-message "'LocalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'LocalObjects::B' first declared as 'struct' here" }
+
+void f ()
+{
+  class A *a1;
+  class A *a2;
+
+  struct B *b1;
+  struct B *b2;
+
+  struct A *a3;             // { dg-warning ".class LocalObjects::A. declared with a mismatched class-key" }
+  class A *a4;
+
+  class B *b3;              // { dg-warning ".struct LocalObjects::B. declared with a mismatched class-key" }
+  struct B *b4;
+}
+}
+
+
+namespace MemberClasses
+{
+struct A { struct B; };
+struct C { struct D; struct D; struct D { }; };
+struct E { class F; class F { }; class F; };
+
+struct G {
+  struct H;                 // { dg-message "'MemberClasses::G::H' first declared as 'struct' here" }
+  class H;                  // { dg-warning "'struct MemberClasses::G::H' declared with a mismatched class-key" }
+  class I { };              // { dg-message "'MemberClasses::G::I' defined as 'class' here" }
+  struct I;                 // { dg-warning "'class MemberClasses::G::I' declared with a mismatched class-key" }
+};
+}
+
+
+namespace DataMembers
+{
+struct A { struct B *p; };
+struct C { struct D *p; struct D *q; struct D { } d; };
+struct E { class F &r; class F { } f; class F *p; };
+
+class G;                    // { dg-message "'DataMembers::G' first declared as 'class' here" }
+struct H;                   // { dg-message "'DataMembers::H' first declared as 'struct' here" }
+
+struct I {
+  struct G *p0;             // { dg-warning "'class DataMembers::G' declared with a mismatched class-key" }
+  class G *p1;
+
+  struct H &r0;
+  class H &r1;              // { dg-warning "'struct DataMembers::H' declared with a mismatched class-key" }
+
+  class J { };              // { dg-message "'DataMembers::I::J' defined as 'class' here" }
+  struct K { };             // { dg-message "'DataMembers::I::K' defined as 'struct' here" }
+
+  class J j0;
+  class K k0;               // { dg-warning "'struct DataMembers::I::K' declared with a mismatched class-key" }
+
+  struct J j1;              // { dg-warning "'class DataMembers::I::J' declared with a mismatched class-key" }
+  struct K k1;
+};
+
+}
+
+
+namespace Templates
+{
+template <int> class A;
+template <int> class A;
+
+template <int> struct B;
+template <int> struct B;
+
+template <int> union C;
+template <int> union C;
+
+template <int> struct D;    // { dg-warning "'class Templates::D<<anonymous> >' declared with a mismatched class-key" }
+template <int>
+class D                     // { dg-message "'Templates::D<<anonymous> >' defined as 'class' here" }
+{ public: D (); };
+
+template <int> class E;     // { dg-warning "'struct Templates::E<<anonymous> >' declared with a mismatched class-key" }
+template <int>
+struct E                    // { dg-message "'Templates::E<<anonymous> >' defined as 'struct' here" }
+{ int i; };
+
+template <int> class D;
+template <int> struct E;
+
+template <int>
+struct D;                   // { dg-warning "'class Templates::D<<anonymous> >' declared with a mismatched class-key" }
+                            // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 }
+}
+
+
+namespace ExplicitSpecializations
+{
+template <int> class A;
+template <> class A<0>;
+template <> struct A<1>;
+template <> struct A<1> { };
+
+template <int> struct B;
+template <> struct B<0>;
+template <> class B<1>;
+template <> class B<2> { public: B (); };
+
+template <int> union C;
+template <> union C<0>;
+
+template <int> class D;
+template <> class D<0>;     // { dg-warning "'struct ExplicitSpecializations::D<0>' declared with a mismatched class-key " }
+template <>
+struct D<0> { };            // { dg-message "'ExplicitSpecializations::D<0>' defined as 'struct' here" }
+
+template <int> struct E;
+template <> struct E<0>;    // { dg-warning "'class ExplicitSpecializations::E<0>' declared with a mismatched class-key" }
+template <>
+class E<0> { };             // { dg-message "'ExplicitSpecializations::E<0>' defined as 'class' here" }
+
+template <int> struct F;
+template <> class F<0> { }; // { dg-message "'ExplicitSpecializations::F<0>' defined as 'class' here" }
+
+template <>
+struct F<0>;                // { dg-warning "'class ExplicitSpecializations::F<0>' declared with a mismatched class-key" }
+}
+
+
+namespace PartialSpecializations
+{
+template <class> class A;
+template <class T> struct A<const T>;
+template <class T> struct A<volatile T>;
+
+template <class> struct B;
+template <class T> class B<const T>;
+template <class T> class B<volatile T>;
+
+template <class> class C { };
+template <class T> struct C<const T> { };
+template <class T> struct C<volatile T> { };
+
+template <class> struct D { };
+template <class T> class D<const T> { };
+template <class T> class D<volatile T> { };
+
+template <class> class E;
+template <class T>
+struct E<const T>;          // { dg-message "note: 'PartialSpecializations::E<const T>' first declared as 'struct' here" }
+
+template <class T>
+class E<const T>;           // { dg-warning "struct PartialSpecializations::E<const T>' declared with a mismatched class-key" }
+
+template <class> class F;
+template <class T>
+class F<const T>;           // { dg-message "'PartialSpecializations::F<const T>' first declared as 'class' here" }
+template <class T>
+struct F<const T>;          // { dg-warning "'class PartialSpecializations::F<const T>' declared with a mismatched class-key" }
+
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C
new file mode 100644
index 00000000000..91d6d4abd54
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C
@@ -0,0 +1,347 @@
+// Test to verify that -Wstruct-not-pod is issued for struct definitions
+// that don't meet the requirements for a POD class.
+// { dg-do compile }
+// { dg-options "-Wstruct-not-pod -ftrack-macro-expansion=0" }
+
+#if __cplusplus > 199711L
+#define ASSERT_POD(result, T) \
+  static_assert (result == __is_pod (T), #T "is pod")
+#else
+#define ASSERT_POD(result, T) \
+  typedef int StaticAssert [1 - 2 * (result != __is_pod (T))]
+#endif
+
+namespace PodStruct
+{
+struct A { }; ASSERT_POD (true, A);
+struct B { int i; const int j; }; ASSERT_POD (true, B);
+struct C { void f (); }; ASSERT_POD (true, C);
+struct D { void operator= (int); }; ASSERT_POD (true, D);
+
+#if __cplusplus > 199711L
+struct E: A { };
+struct F: E { };
+struct G1: private E { }; ASSERT_POD (true, G1);
+struct G2: private F { }; ASSERT_POD (true, G2);
+struct G3: A, B { }; ASSERT_POD (true, G3);
+#endif
+
+struct H { public: int i; }; ASSERT_POD (true, H);
+
+#if __cplusplus > 199711L
+struct I { protected: int i; protected: int j; }; ASSERT_POD (true, I);
+#endif
+
+class J { J (); ~J (); };
+struct K { static const int i; static int &r; static J j; int k; };
+ASSERT_POD (true, K);
+}
+
+
+namespace PodTemplate
+{
+template <class> struct A { };
+template struct A<int>;
+
+template <class> struct B { int i; };
+template struct B<int>;
+
+template <class> struct C { void f (); };
+template struct C<int>;
+
+template <class> struct D { void operator= (int); };
+template struct D<int>;
+
+#if __cplusplus > 199711L
+template <class T> struct E: A<T> { };
+template struct E<int>;
+
+template <class T> struct F: E<T> { };
+template struct F<int>;
+
+template <class T> struct G: private A<T> { };
+template struct G<int>;
+#endif
+
+template <class> struct H { public: int i; };
+template struct H<int>;
+
+#if __cplusplus > 199711L
+template <class> struct I { protected: int i; protected: int j; };
+template struct I<int>;
+#endif
+
+// This is considered a POD even though instantiating it on a non-POD
+// will prevent it from being one.
+template <class T> struct J { T i; };
+template struct J<int>;
+}
+
+
+namespace PodExplicitSpecialization
+{
+template <class> class A;
+template <> struct A<int> { };
+
+template <class> class B;
+template <> struct B<int> { int i; };
+template <class> class C;
+template <> struct C<int> { void f (); };
+template <class> class D;
+template <> struct D<int> { void operator= (int); };
+
+#if __cplusplus > 199711L
+template <class> class E;
+template <> struct E<int>: A<int> { };
+
+template <class> class F;
+template <> struct F<int>: E<int> { };
+
+template <class> class G;
+template <> struct G<int>: private A<int> { };
+#endif
+
+template <class> class H;
+template <> struct H<int> { public: int i; };
+
+#if __cplusplus > 199711L
+template <class> class I;
+template <> struct I<int> { protected: int i; protected: int j; };
+#endif
+
+}
+
+
+namespace PodPartialSpecialization
+{
+template <class> class A;
+template <class T> struct A<const T> { };
+template struct A<const int>;
+
+template <class> class B;
+template <class T> struct B<const T> { int i; };
+template struct B<const int>;
+
+template <class> class C;
+template <class T> struct C<const T> { void f (); };
+template struct C<const int>;
+
+template <class> class D;
+template <class T> struct D<const T> { void operator= (int); };
+template struct D<const int>;
+
+#if __cplusplus > 199711L
+template <class> class E;
+template <class T> struct E<const T>: A<const T> { };
+template struct E<const int>;
+
+template <class> class F;
+template <class T> struct F<const T>: E<const T> { };
+template struct F<const int>;
+
+template <class> class G;
+template <class T> struct G<const T>: private A<const T> { };
+template struct G<const int>;
+#endif
+
+template <class> class H;
+template <class T> struct H<const T> { public: int i; };
+template struct H<const int>;
+
+#if __cplusplus > 199711L
+template <class> class I;
+template <class T> struct I<const T> { protected: int i; protected: int j; };
+template struct I<const int>;
+#endif
+
+// Similar to the case of the primary template, this is considered a POD
+// even though instantiating it on a non-POD will prevent it from being
+// one.
+template <class T> class J;
+template <class T> struct J<const T> { T i; };
+template struct J<const int>;
+}
+
+
+namespace NonPodStructDueToSpecialFunctions
+{
+struct A                // { dg-warning "non-POD type '\[A-Za-z\]\*::A' declared with class-key 'struct'; use 'class' instead" }
+{ A (); };
+
+struct B                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ B (int); };
+
+struct C                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ C (C&); };
+
+struct D                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ ~D (); };
+
+struct E                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ void operator= (E&); };
+}
+
+
+namespace NonPodTemplateDueToSpecialFunctions
+{
+template <class>
+struct A                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ A (); };
+
+template <class>
+struct B                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ B (int); };
+
+template <class>
+struct C                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ C (C&); };
+
+template <class>
+struct D                // { dg-warning "\\\[-Wstruct-not-pod" "FIXME" { xfail *-*-* } }
+{ ~D (); };
+
+template <class>
+struct E                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ void operator= (E&); };
+
+template <class>
+struct F                // { dg-warning "\\\[-Wstruct-not-pod" }
+{
+  template <class T> F (const F<T>&);
+  template <class T> F& operator= (const F<T>&);
+};
+ASSERT_POD (false, F<int>);
+}
+
+
+namespace NonPodExplicitSpecializationDueToSpecialFunctions
+{
+template <class> class A;
+template <>
+struct A<int>           // { dg-warning "\\\[-Wstruct-not-pod" }
+{ A (); };
+
+template <class> class B;
+template <>
+struct B<int>          // { dg-warning "\\\[-Wstruct-not-pod" }
+{ B (int); };
+
+template <class> class C;
+template <>
+struct C<int>           // { dg-warning "\\\[-Wstruct-not-pod" }
+{ C (C&); };
+
+template <class> class D;
+template <>
+struct D<int>           // { dg-warning "\\\[-Wstruct-not-pod" }
+{ ~D (); };
+
+template <class> class E;
+template <>
+struct E<int>           // { dg-warning "\\\[-Wstruct-not-pod" }
+{ void operator= (E&); };
+}
+
+
+namespace NonPodPartialSpecializationDueToSpecialFunctions
+{
+template <class> class A;
+template <class T>
+struct A<T*>            // { dg-warning "\\\[-Wstruct-not-pod" }
+{ A (); };
+template struct A<int*>;
+
+template <class> class B;
+template <class T>
+struct B<T*>            // { dg-warning "\\\[-Wstruct-not-pod" }
+{ B (int); };
+template struct B<int*>;
+
+template <class> class C;
+template <class T>
+struct C<T*>            // { dg-warning "\\\[-Wstruct-not-pod" }
+{ C (C&); };
+template struct C<int*>;
+
+template <class> class D;
+template <class T>
+struct D<T*>            // { dg-warning "\\\[-Wstruct-not-pod" "FIXME" { xfail *-*-* } }
+{ ~D (); };
+template struct D<int*>;
+
+template <class> class E;
+template <class T>
+struct E<T*>            // { dg-warning "\\\[-Wstruct-not-pod" }
+{ void operator= (E&); };
+template struct E<int*>;
+}
+
+
+namespace NonPodDueToVirtuals
+{
+struct A                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ virtual void f (); };
+
+}
+
+
+namespace NonPodDueToNonPodMembers
+{
+struct A                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ int &r; };
+
+class B { public: B (); };
+
+struct C                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ B b; };
+}
+
+
+namespace NonPodTemplateDueToNonPodMembers
+{
+template <class>
+struct A                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ int &r; };
+
+class B { public: B (); };
+
+template <class>
+struct C                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ B b; };
+}
+
+
+namespace NonPodDueToAccess
+{
+struct A                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ int i; private: int j; };
+
+struct B                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ int i; protected: int j; };
+}
+
+
+namespace NonPodTemplateDueToAccess
+{
+template <class>
+struct A                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ int i; private: int j; };
+
+template <class>
+struct B                // { dg-warning "\\\[-Wstruct-not-pod" }
+{ int i; protected: int j; };
+}
+
+
+namespace NonPodDueToBases
+{
+struct A { };
+struct B { };
+struct C: A { };
+struct D: A { };
+struct E: C, D          // { dg-warning "\\\[-Wstruct-not-pod" "pr83374" { xfail *-*-* } }
+{ };
+
+struct F: virtual A     // { dg-warning "\\\[-Wstruct-not-pod" }
+{ };
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]