public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/3] add support for POD struct convention (PR 61339)
@ 2019-07-08 21:58 Martin Sebor
  2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
                   ` (4 more replies)
  0 siblings, 5 replies; 47+ messages in thread
From: Martin Sebor @ 2019-07-08 21:58 UTC (permalink / raw)
  To: gcc-patches

A couple of GCC's Coding Conventions call to

   1) Use the struct keyword for plain old data (POD) types.
      https://www.gnu.org/software/gcc/codingrationale.html#struct

and

   2) Use the class keyword for non-POD types.
      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use

The rationale for the conventions is to convey information about
some basic properties of a user-defined type such as whether it
is safely and efficiently copyable or assignable via memcpy or
whether it requires a call to a copy ctor/assignment operator
or destructor.

A survey of GCC code shows that the convention is only loosely
followed, making it less helpful than it would be otherwise.
In addition, inconsistencies between declarations and definitions
are wide-spread.  Relying on the convention can lead to using
a non-POD class that's declared using the struct class-key in
contexts that aren't prepared for such a type (although not
a direct result, pr90904, 90923, 90959 are examples of some of
the bugs that this might lead to).

The poor consistency with which the convention is adhered to makes
it easy for authors to miss that it exists.  This can then lead to
inefficiencies in code reviews both for reviewers and for authors.

In addition, Clang users who build GCC are bothered by instances
of Clang's -Wmismatched-tags warning (see pr61339 and the recent
patch: https://gcc.gnu.org/ml/gcc-patches/2019-05/msg01805.html).
Clang includes -Wmismatched-tags in -Wall even on Linux.  Although
-Wmismatched-tags is provided for compatibility with Visual C++
(and there to help avoid a potential ABI problem), the warning
points out the same lack of consistency in the GCC coding
convention: the type being sometimes declared as a struct and
other times as a class.

To make the code base consistent with the convention and enable
relying on the struct and class keywords having the intended
meaning, to reduce the time spent in code reviews on details
that an easily be caught earlier by out tools, and to avoid
the -Wmismatched-tags warnings when using Clang, the attached
patch kit does three things:

1) 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).  All three warnings have to be explicitly enabled
    to have an effect.
2) Makes changes to definitions of GCC classes and structs to
    avoid instances of the three new warnings.
3) Changes declarations of GCC classes and structs to match
    the class-key used in their definition.

If/when the patch kit is approved I will enable the three
warnings for GCC builds.  I expect to take the same approach as
with -Wformat-diag: first prevent any remaining instances of
the warnings from triggering errors, clean up or suppress
the remaining instances, and finally enable them to cause errors
with -Werror.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-08 21:58 [PATCH 0/3] add support for POD struct convention (PR 61339) Martin Sebor
@ 2019-07-08 21:59 ` Martin Sebor
  2019-07-22 16:39   ` Martin Sebor
                     ` (2 more replies)
  2019-07-08 22:02 ` [PATCH 2/3] change class-key of PODs to struct and others to class " Martin Sebor
                   ` (3 subsequent siblings)
  4 siblings, 3 replies; 47+ messages in thread
From: Martin Sebor @ 2019-07-08 21:59 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 830 bytes --]

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

[-- Attachment #2: gcc-61339-impl.diff --]
[-- Type: text/x-patch, Size: 41710 bytes --]

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" }
+{ };
+}

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH 2/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-08 21:58 [PATCH 0/3] add support for POD struct convention (PR 61339) Martin Sebor
  2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
@ 2019-07-08 22:02 ` Martin Sebor
  2019-07-08 22:20   ` Martin Sebor
  2019-07-08 22:04 ` [PATCH 3/3] " Martin Sebor
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-08 22:02 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 596 bytes --]

The attached patch changes the class-key of class definitions that
satisfy the requirements on a POD struct to 'struct', and that of
struct definitions that aren't POD to class, according to the GCC
coding convention.  The patch is also prerequisite for GCC being
able to compile cleanly with -Wmismatched-tags.

I made the changes building GCC with -Wstruct-not-pod and
-Wclass-is-pod enabled, scanning the build log for instances
of each warning, and using a script replacing the class-key
as necessary and adjusting the access of the members declared
immediately after the class-head.

Martin

[-- Attachment #2: gcc-61339-pod.diff --]
[-- Type: text/x-patch, Size: 17096 bytes --]

PR c++/61339 - add mismatch between struct and class [-Wmismatched-tags] to non-bugs

gcc/c-family/ChangeLog:

	PR c++/61339
	* c.opt: 

gcc/cp/ChangeLog:

	PR c++/61339
	* parser.c (cp_parser_type_specifier): 
	(cp_parser_function_definition_after_declarator): 
	(cp_parser_template_declaration_after_parameters): 

gcc/testsuite/ChangeLog:

	PR c++/61339
	* g++.dg/warn/Wclass-is-pod.C: New test.
	* g++.dg/warn/Wstruct-not-pod.C: New test.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 080066fa608..27b413115e3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -794,6 +794,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) Init (1) LangEnabledBy(C++ ObjC++, Wall)
+Warn about structs that are not POD.
+
+Wclass-is-pod
+C++ ObjC++ Var(warn_class_is_pod) Init (1) LangEnabledBy(C++ ObjC++, Wall)
+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..e20c26b7ecd 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
@@ -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;
 	}
 
@@ -28039,6 +28043,118 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
   return fn;
 }
 
+/* Return true if the template TYPE appears to meet the requirements
+   of a POD type even if some of its instantiations may not.  */
+
+static bool
+template_pod_p (tree type)
+{
+  if (TYPE_HAS_USER_CONSTRUCTOR (type)
+      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
+      || (TYPE_HAS_COPY_ASSIGN (type)
+	  && (cxx_dialect != cxx98
+	      || !TYPE_HAS_TRIVIAL_COPY_ASSIGN (type))))
+    return false;
+
+  for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+    {
+      if (TREE_CODE (fld) == FIELD_DECL
+	  && !TREE_STATIC (fld)
+	  && TREE_TYPE (fld))
+	{
+	  tree fldtype = TREE_TYPE (fld);
+	  if (TREE_CODE (fldtype) == REFERENCE_TYPE)
+	    return false;
+	  if (TREE_CODE (fldtype) == RECORD_TYPE
+	      && !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;
+
+  const char *key = class_key_or_enum_as_string (type);
+  if (processing_template_decl)
+    {
+      if (template_pod_p (type))
+	{
+	  if (!strcmp (key, "class"))
+	    warning_at (loc, OPT_Wclass_is_pod,
+			"POD-like template %qT declared with class-key %qs; "
+			"use %qs instead",
+			type, key, "struct");
+	  else
+	    inform (loc,
+		    "POD-like template %qT declared with class-key %qs "
+		    "as expected",
+		    type, key);
+	}
+      else if (strcmp (key, "class"))
+	warning_at (loc, OPT_Wstruct_not_pod,
+		    "non-POD-like template %qT declared with class-key %qs; "
+		    "use %qs instead",
+		    type, key, "class");
+      else
+	inform (loc,
+		"non-POD-like template %qT declared with class-key %qs "
+		"as expected",
+		type, key);
+    }
+  else
+    {
+      if (pod_type_p (type))
+	{
+	  if (!strcmp (key, "class"))
+	    warning_at (loc, OPT_Wclass_is_pod,
+			"POD type %qT declared with class-key %qs; "
+			"use %qs instead",
+			type, key, "struct");
+	  else
+	    inform (loc,
+		    "POD type %qT declared with class-key %qs as expected",
+		    type, key);
+	}
+      else if (cxx_dialect == cxx98 && template_pod_p (type))
+	{
+	  if (!strcmp (key, "class"))
+	    warning_at (loc, OPT_Wstruct_not_pod,
+			"C++11 POD type %qT declared with class-key %qs; "
+			"use %qs instead",
+			type, key, "struct");
+	  else
+	    inform (loc,
+		    "C++11 POD type %qT declared with class-key %qs as expected",
+		    type, key);
+	}
+      else if (strcmp (key, "class"))
+	warning_at (loc, OPT_Wstruct_not_pod,
+		    "non-POD type %qT declared with class-key %qs; "
+		    "use %qs instead",
+		    type, key, "class");
+      else
+	inform (loc,
+		"non-POD type %qT declared with class-key %qs as expected",
+		type, key);
+    }
+}
+
 /* Parse a template-declaration body (following argument list).  */
 
 static void
@@ -28076,6 +28192,8 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
 					   member_p,
                                            /*explicit_specialization_p=*/false,
 					   &friend_p);
+      // maybe_warn_struct_vs_class (token->location, TREE_TYPE (decl));
+
       pop_deferring_access_checks ();
 
       /* If this is a member template declaration, let the front
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..c276b469783
--- /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 c++11 } }
+{ };
+
+class D: virtual A
+{ };
+}
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..3e238eedef3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C
@@ -0,0 +1,336 @@
+// 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" }
+
+#define ASSERT_POD(T) static_assert (__is_pod (T), #T "is pod")
+
+namespace PodStruct
+{
+struct A { }; ASSERT_POD (A);
+struct B { int i; const int j; }; ASSERT_POD (B);
+struct C { void f (); }; ASSERT_POD (C);
+struct D { void operator= (int); }; ASSERT_POD (D);
+
+#if __cplusplus > 199711L
+struct E: A { };
+struct F: E { };
+struct G: private A { }; ASSERT_POD (G);
+#endif
+
+struct H { public: int i; }; ASSERT_POD (H);
+
+#if __cplusplus > 199711L
+struct I { protected: int i; protected: int j; }; ASSERT_POD (J);
+#endif
+
+class J { J (); ~J (); };
+struct K { static const int i; static int &r; static J j; int k; };
+ASSERT_POD (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>;
+
+template <class> struct K {
+  /* Template ctor and assignment operator are not special members.  */
+  template <class T> K (const K<T>&);
+  template <class T> K& operator= (const K<T>&);
+};
+ASSERT_POD (K<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&); };
+}
+
+
+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, B          // { dg-warning "\\\[-Wstruct-not-pod" "pr91064" { xfail c++11 } }
+{ };
+
+struct D: virtual A     // { dg-warning "\\\[-Wstruct-not-pod" }
+{ };
+}

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH 3/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-08 21:58 [PATCH 0/3] add support for POD struct convention (PR 61339) Martin Sebor
  2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
  2019-07-08 22:02 ` [PATCH 2/3] change class-key of PODs to struct and others to class " Martin Sebor
@ 2019-07-08 22:04 ` Martin Sebor
  2019-07-09 15:19   ` Richard Sandiford
  2019-07-12  8:41 ` [PATCH 0/3] add support for POD struct convention " Jakub Jelinek
  2019-07-23 16:20 ` Arvind Sankar
  4 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-08 22:04 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 744 bytes --]

The attached patch changes the class-key used in class and struct
declarations and in other declarations and expressions that refer
to them to match the class-key used in their definition.  This
part of the series lets GCC compile with just a small number of
-Wmsmatched-tags instances (down from over 3,000 without it).

I initially tried to make the changes using the same approach as
in part 2 of the series but that didn't work as well so I decided
on a brute force approach using find and sed, and tweaking the rest
by hand.  There are a small number of outstanding inconsistent
declarations that still need to be adjusted.  Most of them are
in generated code that I haven't yet figured how to change to
use the correct class-key.

Martin

[-- Attachment #2: gcc-61339-tag-cleanup.diff.gz --]
[-- Type: application/gzip, Size: 197411 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 2/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-08 22:02 ` [PATCH 2/3] change class-key of PODs to struct and others to class " Martin Sebor
@ 2019-07-08 22:20   ` Martin Sebor
  2019-07-09 14:11     ` Richard Sandiford
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-08 22:20 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 721 bytes --]

Hopefully with the right patch this time (thanks Jon).

On 7/8/19 4:00 PM, Martin Sebor wrote:
> The attached patch changes the class-key of class definitions that
> satisfy the requirements on a POD struct to 'struct', and that of
> struct definitions that aren't POD to class, according to the GCC
> coding convention.  The patch is also prerequisite for GCC being
> able to compile cleanly with -Wmismatched-tags.
> 
> I made the changes building GCC with -Wstruct-not-pod and
> -Wclass-is-pod enabled, scanning the build log for instances
> of each warning, and using a script replacing the class-key
> as necessary and adjusting the access of the members declared
> immediately after the class-head.
> 
> Martin


[-- Attachment #2: gcc-61339-pod-cleanup.diff.gz --]
[-- Type: application/gzip, Size: 36135 bytes --]

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 2/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-08 22:20   ` Martin Sebor
@ 2019-07-09 14:11     ` Richard Sandiford
  2019-07-09 16:37       ` Martin Sebor
  0 siblings, 1 reply; 47+ messages in thread
From: Richard Sandiford @ 2019-07-09 14:11 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches

Martin Sebor <msebor@gmail.com> writes:
> Hopefully with the right patch this time (thanks Jon).
>
> On 7/8/19 4:00 PM, Martin Sebor wrote:
>> The attached patch changes the class-key of class definitions that
>> satisfy the requirements on a POD struct to 'struct', and that of
>> struct definitions that aren't POD to class, according to the GCC
>> coding convention.  The patch is also prerequisite for GCC being
>> able to compile cleanly with -Wmismatched-tags.
>> 
>> I made the changes building GCC with -Wstruct-not-pod and
>> -Wclass-is-pod enabled, scanning the build log for instances
>> of each warning, and using a script replacing the class-key
>> as necessary and adjusting the access of the members declared
>> immediately after the class-head.
>> 
>> Martin
>
> PR c++/61339 - add mismatch between struct and class [-Wmismatched-tags] to non-bugs
>
> gcc/c/ChangeLog:
>
> 	* c-decl.c: Change class-key from class to struct and vice versa
> 	to match convention and avoid -Wclass-is-pod and -Wstruct-no-pod.
> 	* gimple-parser.c: Same.
>
> gcc/c-family/ChangeLog:
>
> 	* c-format.c (check_argument_type): Change class-key from class to
> 	struct and vice versa to match convention and avoid -Wclass-is-pod
> 	and -Wstruct-no-pod.
> 	* c-pretty-print.h: Same.
>
> gcc/cp/ChangeLog:
>
> 	* constexpr.c (cxx_eval_call_expression): Change class-key from class
> 	to struct and vice versa to match convention and avoid -Wclass-is-pod
> 	and -Wstruct-no-pod.
> 	* constraint.cc (get_concept_definition): Same.
> 	* cp-tree.h: Same.
> 	* cxx-pretty-print.h: Same.
> 	* error.c: Same.
> 	* logic.cc (term_list::replace): Same.
> 	* name-lookup.c (find_local_binding): Same.
> 	* pt.c (tsubst_binary_right_fold): Same.
> 	* search.c (field_accessor_p): Same.
> 	* semantics.c (expand_or_defer_fn): Same.
>
> gcc/lto/ChangeLog:
>
> 	* lto-dump.c: Same.

Need to cut-&-paste the description for this one.

> gcc/ChangeLog:
>
> 	* align.h: Change class-key from class to struct and vice versa
> 	to match convention and avoid -Wclass-is-pod and -Wstruct-no-pod.
> 	* alloc-pool.h: Same.
> 	* asan.c (shadow_mem_size): Same.
> 	* auto-profile.c: Same.
> 	* basic-block.h: Same.
> 	* bitmap.h: Same.
> 	* cfgexpand.c (set_rtl): Same.
> 	(expand_one_stack_var_at): Same.
> 	* cfghooks.h: Same.
> 	* cfgloop.h: Same.
> 	* cgraph.h: Same.
> 	* config/i386/i386.h: Same.
> 	* df-problems.c (df_print_bb_index): Same.
> 	* df-scan.c: Same.
> 	* df.h (df_single_use): Same.
> 	* diagnostic-show-locus.c (layout::print_annotation_line): Same.
> 	(layout::annotation_line_showed_range_p): Same.
> 	(get_printed_columns): Same.
> 	(correction::ensure_terminated): Same.
> 	(line_corrections::~line_corrections): Same.
> 	* dojump.h: Same.
> 	* dse.c: Same.
> 	* dump-context.h: Same.
> 	* dumpfile.h: Same.
> 	* dwarf2out.c: Same.
> 	* edit-context.c: Same.
> 	* fibonacci_heap.c (test_union_of_equal_heaps): Same.
> 	* flags.h: Same.
> 	* function.c (assign_stack_local): Same.
> 	* function.h: Same.
> 	* gcc.c: Same.
> 	* gcov.c (block_info::block_info): Same.
> 	* genattrtab.c: Same.
> 	* genextract.c: Same.
> 	* genmatch.c (comparison_code_p): Same.
> 	(id_base::id_base): Same.
> 	(decision_tree::print): Same.
> 	* genoutput.c: Same.
> 	* genpreds.c (write_one_predicate_function): Same.
> 	* genrecog.c (validate_pattern): Same.
> 	(find_operand_positions): Same.
> 	(optimize_subroutine_group): Same.
> 	(merge_pattern_transition::merge_pattern_transition): Same.
> 	(merge_pattern_info::merge_pattern_info): Same.
> 	(merge_state_result::merge_state_result): Same.
> 	(merge_into_state): Same.
> 	* gensupport.c: Same.
> 	* gensupport.h: Same.
> 	* ggc-common.c (init_ggc_heuristics): Same.
> 	* ggc-tests.c (test_union): Same.
> 	* gimple-loop-interchange.cc (dump_induction): Same.
> 	* gimple-loop-versioning.cc: Same.
> 	* gimple-match.h (gimple_match_cond::any_else): Same.
> 	* gimple-ssa-backprop.c: Same.
> 	* gimple-ssa-sprintf.c: Same.
> 	* gimple-ssa-store-merging.c (store_operand_info::store_operand_info): Same.
> 	(store_immediate_info::store_immediate_info): Same.
> 	(merged_store_group::apply_stores): Same.
> 	(get_location_for_stmts): Same.
> 	* gimple-ssa-strength-reduction.c: Same.
> 	* gimple-ssa-warn-alloca.c: Same.
> 	* gimple-ssa-warn-restrict.c (pass_wrestrict::execute): Same.
> 	* godump.c (go_type_decl): Same.
> 	* hash-map-tests.c (test_map_of_strings_to_int): Same.
> 	* hash-map.h: Same.
> 	* hash-set-tests.c (test_set_of_strings): Same.
> 	* hsa-brig.c: Same.
> 	* hsa-common.h: Same.
> 	* hsa-gen.c (transformable_switch_to_sbr_p): Same.
> 	* input.c (assert_loceq): Same.
> 	* input.h: Same.
> 	* ipa-cp.c: Same.
> 	* ipa-devirt.c (possible_polymorphic_call_targets_1): Same.
> 	* ipa-fnsummary.h: Same.
> 	* ipa-inline.h: Same.
> 	* ipa-prop.h: Same.
> 	* ipa-split.c (visit_bb): Same.
> 	* ira-int.h (minmax_set_iter_next): Same.
> 	* loop-invariant.c: Same.
> 	* loop-iv.c: Same.
> 	* lra-eliminations.c: Same.
> 	* lra-int.h: Same.
> 	* lra-lives.c (mark_regno_dead): Same.
> 	* lra-remat.c: Same.
> 	* lra-spills.c: Same.
> 	* lto-streamer.h: Same.
> 	* mem-stats.h: Same.
> 	* omp-grid.c (omp_grid_lastprivate_predicate): Same.
> 	* omp-low.c (omp_clause_aligned_alignment): Same.
> 	* optabs-query.h (get_vcond_eq_icode): Same.
> 	* optabs.h: Same.
> 	* opts.c (wrap_help): Same.
> 	* poly-int.h: Same.
> 	* predict.c (predict_paths_leading_to_edge): Same.
> 	* pretty-print.h: Same.
> 	* profile-count.h: Same.
> 	* read-md.h: Same.
> 	* read-rtl-function.c: Same.
> 	* ree.c: Same.
> 	* reginfo.c: Same.
> 	* regrename.c: Same.
> 	* regrename.h: Same.
> 	* reload.h: Same.
> 	* rtl-iter.h: Same.
> 	* rtl.h (costs_add_n_insns): Same.
> 	* sanopt.c: Same.
> 	* sched-int.h: Same.
> 	* sel-sched-ir.h: Same.
> 	* selftest.h: Same.
> 	* sese.h (vec_find): Same.
> 	* stmt.c: Same.
> 	* target-globals.h: Same.
> 	* tree-affine.c (aff_combination_find_elt): Same.
> 	* tree-affine.h: Same.
> 	* tree-data-ref.h: Same.
> 	* tree-outof-ssa.c (ssa_is_replaceable_p): Same.
> 	* tree-predcom.c: Same.
> 	* tree-scalar-evolution.c (find_var_scev_info): Same.
> 	* tree-ssa-alias.h: Same.
> 	* tree-ssa-ccp.c: Same.
> 	* tree-ssa-coalesce.c (ssa_conflicts_dump): Same.
> 	* tree-ssa-loop-im.c (for_all_locs_in_loop): Same.
> 	(rewrite_mem_refs): Same.
> 	(execute_sm_if_changed): Same.
> 	(hoist_memory_references): Same.
> 	* tree-ssa-loop-ivopts.c (operator<=): Same.
> 	* tree-ssa-loop.h: Same.
> 	* tree-ssa-pre.c (get_or_alloc_expr_for_name): Same.
> 	* tree-ssa-structalias.c: Same.
> 	* tree-switch-conversion.h (cluster::cluster): Same.
> 	(simple_cluster::simple_cluster): Same.
> 	* tree-vect-patterns.c (type_conversion_p): Same.
> 	* tree-vectorizer.c (dump_stmt_cost): Same.
> 	* tree-vectorizer.h (loop_vec_info_for_loop): Same.
> 	* tree.c (protected_set_expr_location): Same.
> 	* tree.h (desired_pro_or_demotion_p): Same.
> 	(fndecl_built_in_p): Same.
> 	* unique-ptr-tests.cc: Same.
> 	* var-tracking.c (delete_variable_part): Same.
> 	* varasm.c (assemble_real): Same.
> 	(tree_output_constant_def): Same.
> 	* vec.c: Same.
> 	* wide-int-bitmask.h: Same.
> 	* wide-int.h (decompose): Same.
>
> libcpp/ChangeLog:
>
> 	* include/line-map.h: Change class-key from class to struct and vice
> 	versa to match convention and avoid -Wclass-is-pod and -Wstruct-no-pod.
> 	* mkdeps.c: Same.yyy

s/yyy// :-)

The changelog format is outdoing itself in usefulness here...

> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index 18839a4a5ec..ca2a34afbae 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -100,7 +100,7 @@ enum symbol_partitioning_class
>  
>  /* Base of all entries in the symbol table.
>     The symtab_node is inherited by cgraph and varpol nodes.  */
> -class GTY((desc ("%h.type"), tag ("SYMTAB_SYMBOL"),
> +struct GTY((desc ("%h.type"), tag ("SYMTAB_SYMBOL"),
>  	   chain_next ("%h.next"), chain_prev ("%h.previous")))
>    symtab_node

Second line should get an extra space of indentation.

> @@ -1673,8 +1675,10 @@ struct GTY(()) cgraph_indirect_call_info
>    unsigned vptr_changed : 1;
>  };
>  
> -struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"),
> -	    for_user)) cgraph_edge {
> +class GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"),
> +	    for_user)) cgraph_edge

Similarly one fewer space here.

> diff --git a/gcc/gcc.c b/gcc/gcc.c
> index 9bd65508b00..9f73ce0e47f 100644
> --- a/gcc/gcc.c
> +++ b/gcc/gcc.c
> @@ -57,7 +57,7 @@ compilation is specified by a string called a "spec".  */
>       getenv ();
>     Hence we need to use "get" for the accessor method, not "getenv".  */
> 
> -class env_manager
> +struct env_manager
>  {
>   public:
>    void init (bool can_restore, bool debug);
> @@ -8574,7 +8574,7 @@ static int n_mdswitches;
>  /* Check whether a particular argument was used.  The first time we
>     canonicalize the switches to keep only the ones we care about.  */
> 
> -class used_arg_t
> +struct used_arg_t
>  {
>   public:
>    int operator () (const char *p, int len);
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index b6e781f7450..e62a9a03ef9 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -130,7 +130,7 @@ template <typename valtype> class ipcp_value;
>  /* Describes a particular source for an IPA-CP value.  */
> 
>  template <typename valtype>
> -class ipcp_value_source
> +struct ipcp_value_source
>  {
>  public:
>    /* Aggregate offset of the source, negative if the source is scalar value of
> @@ -209,7 +209,7 @@ public:
>     contains_variable flag should be disregarded.  */
> 
>  template <typename valtype>
> -class ipcp_lattice
> +struct ipcp_lattice
>  {
>  public:
>    /* The list of known values and types in this lattice.  Note that values are
> @@ -236,7 +236,7 @@ public:
>  /* Lattice of tree values with an offset to describe a part of an
>     aggregate.  */
> 
> -class ipcp_agg_lattice : public ipcp_lattice<tree>
> +struct ipcp_agg_lattice : public ipcp_lattice<tree>
>  {
>  public:
>    /* Offset that is being described by this lattice. */
> diff --git a/gcc/poly-int.h b/gcc/poly-int.h
> index d68a652b5fa..635f1ebeef6 100644
> --- a/gcc/poly-int.h
> +++ b/gcc/poly-int.h
> @@ -335,7 +335,7 @@ struct poly_result<T1, T2, 2>
>  /* A base POD class for polynomial integers.  The polynomial has N
>     coefficients of type C.  */
>  template<unsigned int N, typename C>
> -class poly_int_pod
> +struct poly_int_pod
>  {
>  public:
>    template<typename Ca>
> diff --git a/gcc/profile-count.h b/gcc/profile-count.h
> index cbab5965ed6..e584aab641f 100644
> --- a/gcc/profile-count.h
> +++ b/gcc/profile-count.h
> @@ -676,7 +676,7 @@ public:
>  
>  class sreal;
>  
> -class GTY(()) profile_count
> +struct GTY(()) profile_count
>  {
>  public:
>    /* Use 62bit to hold basic block counters.  Should be at least
> diff --git a/gcc/rtl.h b/gcc/rtl.h
> index 31fba823435..fc1a66416cc 100644
> --- a/gcc/rtl.h
> +++ b/gcc/rtl.h
> @@ -589,7 +594,7 @@ class GTY(()) rtx_nonjump_insn : public rtx_insn
>       from rtl.def.  */
>  };
> 
> -class GTY(()) rtx_jump_insn : public rtx_insn
> +struct GTY(()) rtx_jump_insn : public rtx_insn
>  {
>  public:
>    /* No extra fields, but adds the invariant:
> @@ -533,7 +538,7 @@ is_a_helper <const rtx_sequence *>::test (const_rtx rt)
>    return rt->code == SEQUENCE;
>  }
> 
> -class GTY(()) rtx_insn : public rtx_def
> +struct GTY(()) rtx_insn : public rtx_def
>  {
>  public:
>    /* No extra fields, but adds the invariant:

Might as well get rid of these "public:"s too, unless you feel they
should be kept.

OK with those changes (or without the last one), thanks.

Richard

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 3/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-08 22:04 ` [PATCH 3/3] " Martin Sebor
@ 2019-07-09 15:19   ` Richard Sandiford
  2019-07-09 18:53     ` Martin Sebor
  0 siblings, 1 reply; 47+ messages in thread
From: Richard Sandiford @ 2019-07-09 15:19 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches

Martin Sebor <msebor@gmail.com> writes:
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index cfc41e1ed86..625d5b17413 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6428,7 +6428,7 @@ extern tree get_scope_of_declarator		(const cp_declarator *);
>  extern void grok_special_member_properties	(tree);
>  extern bool grok_ctor_properties		(const_tree, const_tree);
>  extern bool grok_op_properties			(tree, bool);
> -extern tree xref_tag				(enum tag_types, tree, tag_scope, bool, bool * = NULL);
> +extern tree xref_tag				(enum tag_types, tree, tag_scope, bool);
>  extern tree xref_tag_from_type			(tree, tree, tag_scope);
>  extern void xref_basetypes			(tree, tree);
>  extern tree start_enum				(tree, tree, tree, tree, bool, bool *);
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 005f99a6e15..9accc3d141b 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -14119,7 +14119,7 @@ lookup_and_check_tag (enum tag_types tag_code, tree name,
>  
>  static tree
>  xref_tag_1 (enum tag_types tag_code, tree name,
> -            tag_scope scope, bool template_header_p, bool *new_p)
> +            tag_scope scope, bool template_header_p)
>  {
>    enum tree_code code;
>    tree context = NULL_TREE;
> @@ -14151,9 +14151,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>    if (t == error_mark_node)
>      return error_mark_node;
>  
> -  /* Let the caller know this is a new type.  */
> -  *new_p = t == NULL_TREE;
> -
>    if (scope != ts_current && t && current_class_type
>        && template_class_depth (current_class_type)
>        && template_header_p)
> @@ -14215,7 +14212,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>  	      scope = ts_current;
>  	    }
>  	  t = pushtag (name, t, scope);
> -	  *new_p = true;
>  	}
>      }
>    else
> @@ -14267,13 +14263,11 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>  
>  tree
>  xref_tag (enum tag_types tag_code, tree name,
> -          tag_scope scope, bool template_header_p, bool *new_p /* = NULL */)
> +          tag_scope scope, bool template_header_p)
>  {
>    bool dummy;
> -  if (!new_p)
> -    new_p = &dummy;
>    bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
> -  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p, new_p);
> +  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p);
>    timevar_cond_stop (TV_NAME_LOOKUP, subtime);
>    return ret;
>  }
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 52af8c0c6d6..d16bf253058 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -28193,8 +28193,6 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
>  					   member_p,
>                                             /*explicit_specialization_p=*/false,
>  					   &friend_p);
> -      // maybe_warn_struct_vs_class (token->location, TREE_TYPE (decl));
> -
>        pop_deferring_access_checks ();
>  
>        /* If this is a member template declaration, let the front

Looks like this might have been part of 1/3.

OK otherwise.  Thanks again for doing this.

(I guess a lot of these tags could be removed, but that was just as true
before the patch, so it's still a strict improvement.)

Richard

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 2/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-09 14:11     ` Richard Sandiford
@ 2019-07-09 16:37       ` Martin Sebor
  0 siblings, 0 replies; 47+ messages in thread
From: Martin Sebor @ 2019-07-09 16:37 UTC (permalink / raw)
  To: gcc-patches, richard.sandiford

On 7/9/19 7:48 AM, Richard Sandiford wrote:
> Martin Sebor <msebor@gmail.com> writes:
>> Hopefully with the right patch this time (thanks Jon).
>>
>> On 7/8/19 4:00 PM, Martin Sebor wrote:
>>> The attached patch changes the class-key of class definitions that
>>> satisfy the requirements on a POD struct to 'struct', and that of
>>> struct definitions that aren't POD to class, according to the GCC
>>> coding convention.  The patch is also prerequisite for GCC being
>>> able to compile cleanly with -Wmismatched-tags.
>>>
>>> I made the changes building GCC with -Wstruct-not-pod and
>>> -Wclass-is-pod enabled, scanning the build log for instances
>>> of each warning, and using a script replacing the class-key
>>> as necessary and adjusting the access of the members declared
>>> immediately after the class-head.
>>>
>>> Martin
>>
>> PR c++/61339 - add mismatch between struct and class [-Wmismatched-tags] to non-bugs
>>
...
>> gcc/lto/ChangeLog:
>>
>> 	* lto-dump.c: Same.
> 
> Need to cut-&-paste the description for this one.

Done.

...
>>
>> libcpp/ChangeLog:
>>
>> 	* include/line-map.h: Change class-key from class to struct and vice
>> 	versa to match convention and avoid -Wclass-is-pod and -Wstruct-no-pod.
>> 	* mkdeps.c: Same.yyy
> 
> s/yyy// :-)

Ditto.

> 
> The changelog format is outdoing itself in usefulness here...
> 
>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>> index 18839a4a5ec..ca2a34afbae 100644
>> --- a/gcc/cgraph.h
>> +++ b/gcc/cgraph.h
>> @@ -100,7 +100,7 @@ enum symbol_partitioning_class
>>   
>>   /* Base of all entries in the symbol table.
>>      The symtab_node is inherited by cgraph and varpol nodes.  */
>> -class GTY((desc ("%h.type"), tag ("SYMTAB_SYMBOL"),
>> +struct GTY((desc ("%h.type"), tag ("SYMTAB_SYMBOL"),
>>   	   chain_next ("%h.next"), chain_prev ("%h.previous")))
>>     symtab_node
> 
> Second line should get an extra space of indentation.
> 
>> @@ -1673,8 +1675,10 @@ struct GTY(()) cgraph_indirect_call_info
>>     unsigned vptr_changed : 1;
>>   };
>>   
>> -struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"),
>> -	    for_user)) cgraph_edge {
>> +class GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"),
>> +	    for_user)) cgraph_edge
> 
> Similarly one fewer space here.

Done.  My simple script handles just one space issue but not this
one.

...
>> diff --git a/gcc/rtl.h b/gcc/rtl.h
>> index 31fba823435..fc1a66416cc 100644
>> --- a/gcc/rtl.h
>> +++ b/gcc/rtl.h
>> @@ -589,7 +594,7 @@ class GTY(()) rtx_nonjump_insn : public rtx_insn
>>        from rtl.def.  */
>>   };
>>
>> -class GTY(()) rtx_jump_insn : public rtx_insn
>> +struct GTY(()) rtx_jump_insn : public rtx_insn
>>   {
>>   public:
>>     /* No extra fields, but adds the invariant:
>> @@ -533,7 +538,7 @@ is_a_helper <const rtx_sequence *>::test (const_rtx rt)
>>     return rt->code == SEQUENCE;
>>   }
>>
>> -class GTY(()) rtx_insn : public rtx_def
>> +struct GTY(()) rtx_insn : public rtx_def
>>   {
>>   public:
>>     /* No extra fields, but adds the invariant:
> 
> Might as well get rid of these "public:"s too, unless you feel they
> should be kept.

I think my script did that but gengtype choked on the struct when
it had no members so I had to put it back.  I'll try to remember
to reproduce it and open a bug for it.

> 
> OK with those changes (or without the last one), thanks.

Committed in r273308.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 3/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-09 15:19   ` Richard Sandiford
@ 2019-07-09 18:53     ` Martin Sebor
  2019-07-10  9:40       ` Richard Biener
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-09 18:53 UTC (permalink / raw)
  To: gcc-patches, richard.sandiford

On 7/9/19 9:17 AM, Richard Sandiford wrote:
> Martin Sebor <msebor@gmail.com> writes:
>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>> index cfc41e1ed86..625d5b17413 100644
>> --- a/gcc/cp/cp-tree.h
>> +++ b/gcc/cp/cp-tree.h
>> @@ -6428,7 +6428,7 @@ extern tree get_scope_of_declarator		(const cp_declarator *);
>>   extern void grok_special_member_properties	(tree);
>>   extern bool grok_ctor_properties		(const_tree, const_tree);
>>   extern bool grok_op_properties			(tree, bool);
>> -extern tree xref_tag				(enum tag_types, tree, tag_scope, bool, bool * = NULL);
>> +extern tree xref_tag				(enum tag_types, tree, tag_scope, bool);
>>   extern tree xref_tag_from_type			(tree, tree, tag_scope);
>>   extern void xref_basetypes			(tree, tree);
>>   extern tree start_enum				(tree, tree, tree, tree, bool, bool *);
>> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
>> index 005f99a6e15..9accc3d141b 100644
>> --- a/gcc/cp/decl.c
>> +++ b/gcc/cp/decl.c
>> @@ -14119,7 +14119,7 @@ lookup_and_check_tag (enum tag_types tag_code, tree name,
>>   
>>   static tree
>>   xref_tag_1 (enum tag_types tag_code, tree name,
>> -            tag_scope scope, bool template_header_p, bool *new_p)
>> +            tag_scope scope, bool template_header_p)
>>   {
>>     enum tree_code code;
>>     tree context = NULL_TREE;
>> @@ -14151,9 +14151,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>>     if (t == error_mark_node)
>>       return error_mark_node;
>>   
>> -  /* Let the caller know this is a new type.  */
>> -  *new_p = t == NULL_TREE;
>> -
>>     if (scope != ts_current && t && current_class_type
>>         && template_class_depth (current_class_type)
>>         && template_header_p)
>> @@ -14215,7 +14212,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>>   	      scope = ts_current;
>>   	    }
>>   	  t = pushtag (name, t, scope);
>> -	  *new_p = true;
>>   	}
>>       }
>>     else
>> @@ -14267,13 +14263,11 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>>   
>>   tree
>>   xref_tag (enum tag_types tag_code, tree name,
>> -          tag_scope scope, bool template_header_p, bool *new_p /* = NULL */)
>> +          tag_scope scope, bool template_header_p)
>>   {
>>     bool dummy;
>> -  if (!new_p)
>> -    new_p = &dummy;
>>     bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
>> -  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p, new_p);
>> +  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p);
>>     timevar_cond_stop (TV_NAME_LOOKUP, subtime);
>>     return ret;
>>   }
>> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
>> index 52af8c0c6d6..d16bf253058 100644
>> --- a/gcc/cp/parser.c
>> +++ b/gcc/cp/parser.c
>> @@ -28193,8 +28193,6 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
>>   					   member_p,
>>                                              /*explicit_specialization_p=*/false,
>>   					   &friend_p);
>> -      // maybe_warn_struct_vs_class (token->location, TREE_TYPE (decl));
>> -
>>         pop_deferring_access_checks ();
>>   
>>         /* If this is a member template declaration, let the front
> 
> Looks like this might have been part of 1/3.

Yes, this and a few other hunks didn't belong in this patch.
I removed them, retested the patch, and committed r273311.

> 
> OK otherwise.  Thanks again for doing this.
> 
> (I guess a lot of these tags could be removed, but that was just as true
> before the patch, so it's still a strict improvement.)

Most could be removed and my own preference would have been to
remove them.  The warning has a mechanism for figuring out which
ones can one can go and which ones are needed and I considered
making use of it.  In the end I decided to be conservative and
keep them in case someone preferred it that way.  Making
the change now that the cleanup is done will be slightly more
involved.  I suppose we could add yet another warning to find
them: -Wredundant-tag.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 3/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-09 18:53     ` Martin Sebor
@ 2019-07-10  9:40       ` Richard Biener
  2019-07-10 10:52         ` Richard Sandiford
  0 siblings, 1 reply; 47+ messages in thread
From: Richard Biener @ 2019-07-10  9:40 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches, Richard Sandiford

On Tue, Jul 9, 2019 at 8:34 PM Martin Sebor <msebor@gmail.com> wrote:
>
> On 7/9/19 9:17 AM, Richard Sandiford wrote:
> > Martin Sebor <msebor@gmail.com> writes:
> >> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> >> index cfc41e1ed86..625d5b17413 100644
> >> --- a/gcc/cp/cp-tree.h
> >> +++ b/gcc/cp/cp-tree.h
> >> @@ -6428,7 +6428,7 @@ extern tree get_scope_of_declarator            (const cp_declarator *);
> >>   extern void grok_special_member_properties (tree);
> >>   extern bool grok_ctor_properties           (const_tree, const_tree);
> >>   extern bool grok_op_properties                     (tree, bool);
> >> -extern tree xref_tag                                (enum tag_types, tree, tag_scope, bool, bool * = NULL);
> >> +extern tree xref_tag                                (enum tag_types, tree, tag_scope, bool);
> >>   extern tree xref_tag_from_type                     (tree, tree, tag_scope);
> >>   extern void xref_basetypes                 (tree, tree);
> >>   extern tree start_enum                             (tree, tree, tree, tree, bool, bool *);
> >> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> >> index 005f99a6e15..9accc3d141b 100644
> >> --- a/gcc/cp/decl.c
> >> +++ b/gcc/cp/decl.c
> >> @@ -14119,7 +14119,7 @@ lookup_and_check_tag (enum tag_types tag_code, tree name,
> >>
> >>   static tree
> >>   xref_tag_1 (enum tag_types tag_code, tree name,
> >> -            tag_scope scope, bool template_header_p, bool *new_p)
> >> +            tag_scope scope, bool template_header_p)
> >>   {
> >>     enum tree_code code;
> >>     tree context = NULL_TREE;
> >> @@ -14151,9 +14151,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
> >>     if (t == error_mark_node)
> >>       return error_mark_node;
> >>
> >> -  /* Let the caller know this is a new type.  */
> >> -  *new_p = t == NULL_TREE;
> >> -
> >>     if (scope != ts_current && t && current_class_type
> >>         && template_class_depth (current_class_type)
> >>         && template_header_p)
> >> @@ -14215,7 +14212,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
> >>            scope = ts_current;
> >>          }
> >>        t = pushtag (name, t, scope);
> >> -      *new_p = true;
> >>      }
> >>       }
> >>     else
> >> @@ -14267,13 +14263,11 @@ xref_tag_1 (enum tag_types tag_code, tree name,
> >>
> >>   tree
> >>   xref_tag (enum tag_types tag_code, tree name,
> >> -          tag_scope scope, bool template_header_p, bool *new_p /* = NULL */)
> >> +          tag_scope scope, bool template_header_p)
> >>   {
> >>     bool dummy;
> >> -  if (!new_p)
> >> -    new_p = &dummy;
> >>     bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
> >> -  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p, new_p);
> >> +  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p);
> >>     timevar_cond_stop (TV_NAME_LOOKUP, subtime);
> >>     return ret;
> >>   }
> >> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> >> index 52af8c0c6d6..d16bf253058 100644
> >> --- a/gcc/cp/parser.c
> >> +++ b/gcc/cp/parser.c
> >> @@ -28193,8 +28193,6 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
> >>                                         member_p,
> >>                                              /*explicit_specialization_p=*/false,
> >>                                         &friend_p);
> >> -      // maybe_warn_struct_vs_class (token->location, TREE_TYPE (decl));
> >> -
> >>         pop_deferring_access_checks ();
> >>
> >>         /* If this is a member template declaration, let the front
> >
> > Looks like this might have been part of 1/3.
>
> Yes, this and a few other hunks didn't belong in this patch.
> I removed them, retested the patch, and committed r273311.
>
> >
> > OK otherwise.  Thanks again for doing this.
> >
> > (I guess a lot of these tags could be removed, but that was just as true
> > before the patch, so it's still a strict improvement.)
>
> Most could be removed and my own preference would have been to
> remove them.  The warning has a mechanism for figuring out which
> ones can one can go and which ones are needed and I considered
> making use of it.  In the end I decided to be conservative and
> keep them in case someone preferred it that way.  Making
> the change now that the cleanup is done will be slightly more
> involved.  I suppose we could add yet another warning to find
> them: -Wredundant-tag.

Just to pick one - why is struct loop not a POD?  Because of its
widest_int members?  But then we allocate it with
ggc_cleared_alloc<class loop> () which AFAICS doesn't
invoke a constructor (and I hope it doesn't trigger the finalization
path).

Richard.

>
> Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 3/3] change class-key of PODs to struct and others to class (PR 61339)
  2019-07-10  9:40       ` Richard Biener
@ 2019-07-10 10:52         ` Richard Sandiford
  0 siblings, 0 replies; 47+ messages in thread
From: Richard Sandiford @ 2019-07-10 10:52 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Sebor, gcc-patches

Richard Biener <richard.guenther@gmail.com> writes:
> On Tue, Jul 9, 2019 at 8:34 PM Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 7/9/19 9:17 AM, Richard Sandiford wrote:
>> > Martin Sebor <msebor@gmail.com> writes:
>> >> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>> >> index cfc41e1ed86..625d5b17413 100644
>> >> --- a/gcc/cp/cp-tree.h
>> >> +++ b/gcc/cp/cp-tree.h
>> >> @@ -6428,7 +6428,7 @@ extern tree get_scope_of_declarator            (const cp_declarator *);
>> >>   extern void grok_special_member_properties (tree);
>> >>   extern bool grok_ctor_properties           (const_tree, const_tree);
>> >>   extern bool grok_op_properties                     (tree, bool);
>> >> -extern tree xref_tag                                (enum tag_types, tree, tag_scope, bool, bool * = NULL);
>> >> +extern tree xref_tag                                (enum tag_types, tree, tag_scope, bool);
>> >>   extern tree xref_tag_from_type                     (tree, tree, tag_scope);
>> >>   extern void xref_basetypes                 (tree, tree);
>> >>   extern tree start_enum                             (tree, tree, tree, tree, bool, bool *);
>> >> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
>> >> index 005f99a6e15..9accc3d141b 100644
>> >> --- a/gcc/cp/decl.c
>> >> +++ b/gcc/cp/decl.c
>> >> @@ -14119,7 +14119,7 @@ lookup_and_check_tag (enum tag_types tag_code, tree name,
>> >>
>> >>   static tree
>> >>   xref_tag_1 (enum tag_types tag_code, tree name,
>> >> -            tag_scope scope, bool template_header_p, bool *new_p)
>> >> +            tag_scope scope, bool template_header_p)
>> >>   {
>> >>     enum tree_code code;
>> >>     tree context = NULL_TREE;
>> >> @@ -14151,9 +14151,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>> >>     if (t == error_mark_node)
>> >>       return error_mark_node;
>> >>
>> >> -  /* Let the caller know this is a new type.  */
>> >> -  *new_p = t == NULL_TREE;
>> >> -
>> >>     if (scope != ts_current && t && current_class_type
>> >>         && template_class_depth (current_class_type)
>> >>         && template_header_p)
>> >> @@ -14215,7 +14212,6 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>> >>            scope = ts_current;
>> >>          }
>> >>        t = pushtag (name, t, scope);
>> >> -      *new_p = true;
>> >>      }
>> >>       }
>> >>     else
>> >> @@ -14267,13 +14263,11 @@ xref_tag_1 (enum tag_types tag_code, tree name,
>> >>
>> >>   tree
>> >>   xref_tag (enum tag_types tag_code, tree name,
>> >> -          tag_scope scope, bool template_header_p, bool *new_p /* = NULL */)
>> >> +          tag_scope scope, bool template_header_p)
>> >>   {
>> >>     bool dummy;
>> >> -  if (!new_p)
>> >> -    new_p = &dummy;
>> >>     bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
>> >> -  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p, new_p);
>> >> +  tree ret = xref_tag_1 (tag_code, name, scope, template_header_p);
>> >>     timevar_cond_stop (TV_NAME_LOOKUP, subtime);
>> >>     return ret;
>> >>   }
>> >> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
>> >> index 52af8c0c6d6..d16bf253058 100644
>> >> --- a/gcc/cp/parser.c
>> >> +++ b/gcc/cp/parser.c
>> >> @@ -28193,8 +28193,6 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
>> >>                                         member_p,
>> >>                                              /*explicit_specialization_p=*/false,
>> >>                                         &friend_p);
>> >> -      // maybe_warn_struct_vs_class (token->location, TREE_TYPE (decl));
>> >> -
>> >>         pop_deferring_access_checks ();
>> >>
>> >>         /* If this is a member template declaration, let the front
>> >
>> > Looks like this might have been part of 1/3.
>>
>> Yes, this and a few other hunks didn't belong in this patch.
>> I removed them, retested the patch, and committed r273311.
>>
>> >
>> > OK otherwise.  Thanks again for doing this.
>> >
>> > (I guess a lot of these tags could be removed, but that was just as true
>> > before the patch, so it's still a strict improvement.)
>>
>> Most could be removed and my own preference would have been to
>> remove them.  The warning has a mechanism for figuring out which
>> ones can one can go and which ones are needed and I considered
>> making use of it.  In the end I decided to be conservative and
>> keep them in case someone preferred it that way.  Making
>> the change now that the cleanup is done will be slightly more
>> involved.  I suppose we could add yet another warning to find
>> them: -Wredundant-tag.
>
> Just to pick one - why is struct loop not a POD?  Because of its
> widest_int members?

Yeah.

> But then we allocate it with ggc_cleared_alloc<class loop> () which
> AFAICS doesn't invoke a constructor

Yeah, here and elsewhere we have a habit of initialising non-PODs
without using the proper constructor.

> (and I hope it doesn't trigger the finalization path).

Yeah, but that's based on whether it has a trivial destructor rather
than whether it's POD.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-08 21:58 [PATCH 0/3] add support for POD struct convention (PR 61339) Martin Sebor
                   ` (2 preceding siblings ...)
  2019-07-08 22:04 ` [PATCH 3/3] " Martin Sebor
@ 2019-07-12  8:41 ` Jakub Jelinek
  2019-07-12 11:44   ` Jonathan Wakely
  2019-08-14 18:50   ` Pedro Alves
  2019-07-23 16:20 ` Arvind Sankar
  4 siblings, 2 replies; 47+ messages in thread
From: Jakub Jelinek @ 2019-07-12  8:41 UTC (permalink / raw)
  To: Martin Sebor, Jonathan Wakely, Jason Merrill, Nathan Sidwell,
	Richard Biener
  Cc: gcc-patches

On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
> A couple of GCC's Coding Conventions call to
> 
>   1) Use the struct keyword for plain old data (POD) types.
>      https://www.gnu.org/software/gcc/codingrationale.html#struct
> 
> and
> 
>   2) Use the class keyword for non-POD types.
>      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use

This is a coding convention that has been added without any discussion
whatsoever on that, maybe it was some Google internal coding convention or
something, do we really want to enforce it rather than discuss
and decide what we actually want?

With my limited C++ knowledge, the main distinction between struct and class
when both can appear interchangeably is that struct defaults to public:
and class defaults to private:, and I think it is best to use those that
way, rather than having tons of class ... { public: ... } everywhere.

There are many C++ class boolean properties, rather than just
POD vs. non-POD and we could pick any of them instead of this particular one
for the struct vs. class distinction if we wanted to enforce it, but why?

I'd just arrange that when being compiled with clang we compile with
-Wno-mismatched-tags to get rid of their misdesigned warning and not add
such misdesigned warning to GCC, that will just help people spread this
weirdo requirement further.

	Jakub

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12  8:41 ` [PATCH 0/3] add support for POD struct convention " Jakub Jelinek
@ 2019-07-12 11:44   ` Jonathan Wakely
  2019-07-12 15:14     ` Jason Merrill
  2019-07-12 15:26     ` Martin Sebor
  2019-08-14 18:50   ` Pedro Alves
  1 sibling, 2 replies; 47+ messages in thread
From: Jonathan Wakely @ 2019-07-12 11:44 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Martin Sebor, Jason Merrill, Nathan Sidwell, Richard Biener, gcc-patches

On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
>On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
>> A couple of GCC's Coding Conventions call to
>>
>>   1) Use the struct keyword for plain old data (POD) types.
>>      https://www.gnu.org/software/gcc/codingrationale.html#struct
>>
>> and
>>
>>   2) Use the class keyword for non-POD types.
>>      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
>
>This is a coding convention that has been added without any discussion
>whatsoever on that, maybe it was some Google internal coding convention or
>something, do we really want to enforce it rather than discuss
>and decide what we actually want?
>
>With my limited C++ knowledge, the main distinction between struct and class
>when both can appear interchangeably is that struct defaults to public:

The default applies to class members and base classes, but that's the
*only* distinction.

>and class defaults to private:, and I think it is best to use those that
>way, rather than having tons of class ... { public: ... } everywhere.
>
>There are many C++ class boolean properties, rather than just
>POD vs. non-POD and we could pick any of them instead of this particular one
>for the struct vs. class distinction if we wanted to enforce it, but why?

I'm also unconvinced that POD is a useful distinction. A class might
be a POD today, but then we decide to add a default constructor to it
(or if/when we move to C++11, add default member initializers to it).
Does that mean we need to replace struct with class to follow this
convention?

Or we might decide to add a std::string member to it, which stops it
being a POD. Should every reference to it be changed from struct to
class?

Personally I think "no user-defined constructors" might be a more
useful distinction than POD. This is not a POD (because of the
std::string member) but I don't see why it should be a class not a
struct:

struct A {
  std::string name;
  int id;
};


>I'd just arrange that when being compiled with clang we compile with
>-Wno-mismatched-tags to get rid of their misdesigned warning and not add
>such misdesigned warning to GCC, that will just help people spread this
>weirdo requirement further.
>
>	Jakub

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12 11:44   ` Jonathan Wakely
@ 2019-07-12 15:14     ` Jason Merrill
  2019-07-12 15:26     ` Martin Sebor
  1 sibling, 0 replies; 47+ messages in thread
From: Jason Merrill @ 2019-07-12 15:14 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Jakub Jelinek, Martin Sebor, Nathan Sidwell, Richard Biener, gcc-patches

On Fri, Jul 12, 2019 at 7:42 AM Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
> >On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
> >> A couple of GCC's Coding Conventions call to
> >>
> >>   1) Use the struct keyword for plain old data (POD) types.
> >>      https://www.gnu.org/software/gcc/codingrationale.html#struct
> >>
> >> and
> >>
> >>   2) Use the class keyword for non-POD types.
> >>      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
> >
> >This is a coding convention that has been added without any discussion
> >whatsoever on that, maybe it was some Google internal coding convention or
> >something, do we really want to enforce it rather than discuss
> >and decide what we actually want?
> >
> >With my limited C++ knowledge, the main distinction between struct and class
> >when both can appear interchangeably is that struct defaults to public:
>
> The default applies to class members and base classes, but that's the
> *only* distinction.
>
> >and class defaults to private:, and I think it is best to use those that
> >way, rather than having tons of class ... { public: ... } everywhere.
> >
> >There are many C++ class boolean properties, rather than just
> >POD vs. non-POD and we could pick any of them instead of this particular one
> >for the struct vs. class distinction if we wanted to enforce it, but why?

I agree.  The class-key used to define a class has very little signalling value.

> I'm also unconvinced that POD is a useful distinction. A class might
> be a POD today, but then we decide to add a default constructor to it
> (or if/when we move to C++11, add default member initializers to it).
> Does that mean we need to replace struct with class to follow this
> convention?
>
> Or we might decide to add a std::string member to it, which stops it
> being a POD. Should every reference to it be changed from struct to
> class?

Indeed.

> Personally I think "no user-defined constructors" might be a more
> useful distinction than POD. This is not a POD (because of the
> std::string member) but I don't see why it should be a class not a
> struct:
>
> struct A {
>   std::string name;
>   int id;
> };

In other words, "aggregate".

But I'm not sure even that is useful as a rule.  If someone wants to
add a constructor to their aggregate to allow parenthesized
initialization, but otherwise use the data members directly, having to
add "public:" at the top is useless boilerplate.

> >I'd just arrange that when being compiled with clang we compile with
> >-Wno-mismatched-tags to get rid of their misdesigned warning

Perhaps, but we might as well use the same tag.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12 11:44   ` Jonathan Wakely
  2019-07-12 15:14     ` Jason Merrill
@ 2019-07-12 15:26     ` Martin Sebor
  2019-07-12 15:36       ` Jonathan Wakely
  2019-07-12 15:40       ` Jason Merrill
  1 sibling, 2 replies; 47+ messages in thread
From: Martin Sebor @ 2019-07-12 15:26 UTC (permalink / raw)
  To: Jonathan Wakely, Jakub Jelinek
  Cc: Jason Merrill, Nathan Sidwell, Richard Biener, gcc-patches

On 7/12/19 5:42 AM, Jonathan Wakely wrote:
> On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
>> On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
>>> A couple of GCC's Coding Conventions call to
>>>
>>>   1) Use the struct keyword for plain old data (POD) types.
>>>      https://www.gnu.org/software/gcc/codingrationale.html#struct
>>>
>>> and
>>>
>>>   2) Use the class keyword for non-POD types.
>>>      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
>>
>> This is a coding convention that has been added without any discussion
>> whatsoever on that, maybe it was some Google internal coding 
>> convention or
>> something, do we really want to enforce it rather than discuss
>> and decide what we actually want?
>>
>> With my limited C++ knowledge, the main distinction between struct and 
>> class
>> when both can appear interchangeably is that struct defaults to public:
> 
> The default applies to class members and base classes, but that's the
> *only* distinction.
> 
>> and class defaults to private:, and I think it is best to use those that
>> way, rather than having tons of class ... { public: ... } everywhere.
>>
>> There are many C++ class boolean properties, rather than just
>> POD vs. non-POD and we could pick any of them instead of this 
>> particular one
>> for the struct vs. class distinction if we wanted to enforce it, but why?
> 
> I'm also unconvinced that POD is a useful distinction. A class might
> be a POD today, but then we decide to add a default constructor to it
> (or if/when we move to C++11, add default member initializers to it).
> Does that mean we need to replace struct with class to follow this
> convention?
> 
> Or we might decide to add a std::string member to it, which stops it
> being a POD. Should every reference to it be changed from struct to
> class?

Right, that's a downside of such a convention.  It can be mitigated
(but not avoided completely) by eschewing the class-key in references
to the type if it's unambiguous.  The warning suggests to drop
the class-key when it's possible.  I didn't follow that suggestion
in the cleanup patch only out of concern that people used to seeing
the 'class' or 'struct' there might be bothered by its removal.

At the same time, adding a std::string member to A POD can have
serious ripple effects in a code base that assumes the struct is
a POD.  -Wclass-memaccess finds a subset of those but not nearly
all of them (I'd love to see a warning that detected more).

> Personally I think "no user-defined constructors" might be a more
> useful distinction than POD. This is not a POD (because of the
> std::string member) but I don't see why it should be a class not a
> struct:
> 
> struct A {
>   std::string name;
>   int id;
> };

It looks like a fine struct to me :)

But the answer depends on the goal of the convention.  If it is
to make it clear to users of A whether it can be used in a context
that expects a "POD" (such as some GCC containers) then A would
have to be a class.  Ideally, the POD requirement would be enforced
by smarter warnings and/or by type traits and static assertions but
not all projects make use of those (yet).

 From my personal POV, the main value of the warnings is in
helping achieve the consistency that GCC (and many other projects)
put a heavy emphasis on.  To that end, all one really needs is
-Wmismatched-tags.  In the absence of the POD struct convention,
the other two warnings might only help decide what class-key to
choose when defining a new type.

Martin

PS I'm using the term POD loosely here.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12 15:26     ` Martin Sebor
@ 2019-07-12 15:36       ` Jonathan Wakely
  2019-07-12 16:29         ` Martin Sebor
  2019-07-12 15:40       ` Jason Merrill
  1 sibling, 1 reply; 47+ messages in thread
From: Jonathan Wakely @ 2019-07-12 15:36 UTC (permalink / raw)
  To: Martin Sebor
  Cc: Jakub Jelinek, Jason Merrill, Nathan Sidwell, Richard Biener,
	gcc-patches

On 12/07/19 09:14 -0600, Martin Sebor wrote:
>On 7/12/19 5:42 AM, Jonathan Wakely wrote:
>>On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
>>>On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
>>>>A couple of GCC's Coding Conventions call to
>>>>
>>>>  1) Use the struct keyword for plain old data (POD) types.
>>>>     https://www.gnu.org/software/gcc/codingrationale.html#struct
>>>>
>>>>and
>>>>
>>>>  2) Use the class keyword for non-POD types.
>>>>     https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
>>>
>>>This is a coding convention that has been added without any discussion
>>>whatsoever on that, maybe it was some Google internal coding 
>>>convention or
>>>something, do we really want to enforce it rather than discuss
>>>and decide what we actually want?
>>>
>>>With my limited C++ knowledge, the main distinction between struct 
>>>and class
>>>when both can appear interchangeably is that struct defaults to public:
>>
>>The default applies to class members and base classes, but that's the
>>*only* distinction.
>>
>>>and class defaults to private:, and I think it is best to use those that
>>>way, rather than having tons of class ... { public: ... } everywhere.
>>>
>>>There are many C++ class boolean properties, rather than just
>>>POD vs. non-POD and we could pick any of them instead of this 
>>>particular one
>>>for the struct vs. class distinction if we wanted to enforce it, but why?
>>
>>I'm also unconvinced that POD is a useful distinction. A class might
>>be a POD today, but then we decide to add a default constructor to it
>>(or if/when we move to C++11, add default member initializers to it).
>>Does that mean we need to replace struct with class to follow this
>>convention?
>>
>>Or we might decide to add a std::string member to it, which stops it
>>being a POD. Should every reference to it be changed from struct to
>>class?
>
>Right, that's a downside of such a convention.  It can be mitigated
>(but not avoided completely) by eschewing the class-key in references
>to the type if it's unambiguous.

Well IMO the class-key should never be used except in declarations of
the type. I dislike seeing 'struct X x = { ... };' in C++ because the
'struct' is superfluous. I prefer to avoid naming ambiguities that
require the 'struct' there (e.g. stat(3) and struct stat). If GCC has
any structs that are using the same name as a function I'd suggest
renaming the struct or the function :-)

So the only time you should have 'struct X' is when declaring it
(including in its definition). And for that case being consistent to
silence -Wmismatched-tags is enough (and while not technically
necessary, it's also harmless to be consistent).

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12 15:26     ` Martin Sebor
  2019-07-12 15:36       ` Jonathan Wakely
@ 2019-07-12 15:40       ` Jason Merrill
  2019-07-12 16:49         ` Martin Sebor
  1 sibling, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-07-12 15:40 UTC (permalink / raw)
  To: Martin Sebor
  Cc: Jonathan Wakely, Jakub Jelinek, Nathan Sidwell, Richard Biener,
	gcc-patches

On Fri, Jul 12, 2019 at 11:14 AM Martin Sebor <msebor@gmail.com> wrote:
>
> On 7/12/19 5:42 AM, Jonathan Wakely wrote:
> > On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
> >> On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
> >>> A couple of GCC's Coding Conventions call to
> >>>
> >>>   1) Use the struct keyword for plain old data (POD) types.
> >>>      https://www.gnu.org/software/gcc/codingrationale.html#struct
> >>>
> >>> and
> >>>
> >>>   2) Use the class keyword for non-POD types.
> >>>      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
> >>
> >> This is a coding convention that has been added without any discussion
> >> whatsoever on that, maybe it was some Google internal coding
> >> convention or
> >> something, do we really want to enforce it rather than discuss
> >> and decide what we actually want?
> >>
> >> With my limited C++ knowledge, the main distinction between struct and
> >> class
> >> when both can appear interchangeably is that struct defaults to public:
> >
> > The default applies to class members and base classes, but that's the
> > *only* distinction.
> >
> >> and class defaults to private:, and I think it is best to use those that
> >> way, rather than having tons of class ... { public: ... } everywhere.
> >>
> >> There are many C++ class boolean properties, rather than just
> >> POD vs. non-POD and we could pick any of them instead of this
> >> particular one
> >> for the struct vs. class distinction if we wanted to enforce it, but why?
> >
> > I'm also unconvinced that POD is a useful distinction. A class might
> > be a POD today, but then we decide to add a default constructor to it
> > (or if/when we move to C++11, add default member initializers to it).
> > Does that mean we need to replace struct with class to follow this
> > convention?
> >
> > Or we might decide to add a std::string member to it, which stops it
> > being a POD. Should every reference to it be changed from struct to
> > class?
>
> Right, that's a downside of such a convention.  It can be mitigated
> (but not avoided completely) by eschewing the class-key in references
> to the type if it's unambiguous.  The warning suggests to drop
> the class-key when it's possible.  I didn't follow that suggestion
> in the cleanup patch only out of concern that people used to seeing
> the 'class' or 'struct' there might be bothered by its removal.
>
> At the same time, adding a std::string member to A POD can have
> serious ripple effects in a code base that assumes the struct is
> a POD.  -Wclass-memaccess finds a subset of those but not nearly
> all of them (I'd love to see a warning that detected more).
>
> > Personally I think "no user-defined constructors" might be a more
> > useful distinction than POD. This is not a POD (because of the
> > std::string member) but I don't see why it should be a class not a
> > struct:
> >
> > struct A {
> >   std::string name;
> >   int id;
> > };
>
> It looks like a fine struct to me :)
>
> But the answer depends on the goal of the convention.  If it is
> to make it clear to users of A whether it can be used in a context
> that expects a "POD" (such as some GCC containers) then A would
> have to be a class.  Ideally, the POD requirement would be enforced
> by smarter warnings and/or by type traits and static assertions but
> not all projects make use of those (yet).

If a container has requirements of an element class, it should enforce
them with STATIC_ASSERT.  Trying to express this distinction with
'struct' vs 'class' seems obscure and fragile; that's an attribute of
a class that can't be checked by the actual code.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12 15:36       ` Jonathan Wakely
@ 2019-07-12 16:29         ` Martin Sebor
  0 siblings, 0 replies; 47+ messages in thread
From: Martin Sebor @ 2019-07-12 16:29 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Jakub Jelinek, Jason Merrill, Nathan Sidwell, Richard Biener,
	gcc-patches

On 7/12/19 9:26 AM, Jonathan Wakely wrote:
> On 12/07/19 09:14 -0600, Martin Sebor wrote:
>> On 7/12/19 5:42 AM, Jonathan Wakely wrote:
>>> On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
>>>> On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
>>>>> A couple of GCC's Coding Conventions call to
>>>>>
>>>>>   1) Use the struct keyword for plain old data (POD) types.
>>>>>      https://www.gnu.org/software/gcc/codingrationale.html#struct
>>>>>
>>>>> and
>>>>>
>>>>>   2) Use the class keyword for non-POD types.
>>>>>      https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
>>>>
>>>> This is a coding convention that has been added without any discussion
>>>> whatsoever on that, maybe it was some Google internal coding 
>>>> convention or
>>>> something, do we really want to enforce it rather than discuss
>>>> and decide what we actually want?
>>>>
>>>> With my limited C++ knowledge, the main distinction between struct 
>>>> and class
>>>> when both can appear interchangeably is that struct defaults to public:
>>>
>>> The default applies to class members and base classes, but that's the
>>> *only* distinction.
>>>
>>>> and class defaults to private:, and I think it is best to use those 
>>>> that
>>>> way, rather than having tons of class ... { public: ... } everywhere.
>>>>
>>>> There are many C++ class boolean properties, rather than just
>>>> POD vs. non-POD and we could pick any of them instead of this 
>>>> particular one
>>>> for the struct vs. class distinction if we wanted to enforce it, but 
>>>> why?
>>>
>>> I'm also unconvinced that POD is a useful distinction. A class might
>>> be a POD today, but then we decide to add a default constructor to it
>>> (or if/when we move to C++11, add default member initializers to it).
>>> Does that mean we need to replace struct with class to follow this
>>> convention?
>>>
>>> Or we might decide to add a std::string member to it, which stops it
>>> being a POD. Should every reference to it be changed from struct to
>>> class?
>>
>> Right, that's a downside of such a convention.  It can be mitigated
>> (but not avoided completely) by eschewing the class-key in references
>> to the type if it's unambiguous.
> 
> Well IMO the class-key should never be used except in declarations of
> the type. I dislike seeing 'struct X x = { ... };' in C++ because the
> 'struct' is superfluous. I prefer to avoid naming ambiguities that
> require the 'struct' there (e.g. stat(3) and struct stat). If GCC has
> any structs that are using the same name as a function I'd suggest
> renaming the struct or the function :-)

There are a few such ambiguities, not just with functions but also
with variables.  One I have run into a few times is class vr_values
and the vrp_prop::vr_values and vrp_folder::vr_values* data members,
and the vr_values* local variable/function argument used is many
places in tree-vrp.c (I'm not picking on VRP here; there are others).

I think it's a fairly common convention in C but I myself find
code clearer without such overloading, and in C++ it helps avoid
the annoying name clash.  I could add yet another warning to help
clean that up.

> So the only time you should have 'struct X' is when declaring it
> (including in its definition). And for that case being consistent to
> silence -Wmismatched-tags is enough (and while not technically
> necessary, it's also harmless to be consistent).

Agreed.  With the additional cleanup above this sounds like a good
guideline for "pure" C++ code to adopt.  For mixed C/C++ code or
code transitioning from C to C++, completely getting away from
using the class-key in all contexts may not be possible or might
take some time.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12 15:40       ` Jason Merrill
@ 2019-07-12 16:49         ` Martin Sebor
  0 siblings, 0 replies; 47+ messages in thread
From: Martin Sebor @ 2019-07-12 16:49 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Jonathan Wakely, Jakub Jelinek, Nathan Sidwell, Richard Biener,
	gcc-patches

On 7/12/19 9:36 AM, Jason Merrill wrote:
> On Fri, Jul 12, 2019 at 11:14 AM Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 7/12/19 5:42 AM, Jonathan Wakely wrote:
>>> On 12/07/19 10:24 +0200, Jakub Jelinek wrote:
>>>> On Mon, Jul 08, 2019 at 03:56:51PM -0600, Martin Sebor wrote:
>>>>> A couple of GCC's Coding Conventions call to
>>>>>
>>>>>    1) Use the struct keyword for plain old data (POD) types.
>>>>>       https://www.gnu.org/software/gcc/codingrationale.html#struct
>>>>>
>>>>> and
>>>>>
>>>>>    2) Use the class keyword for non-POD types.
>>>>>       https://www.gnu.org/software/gcc/codingconventions.html#Class_Use
>>>>
>>>> This is a coding convention that has been added without any discussion
>>>> whatsoever on that, maybe it was some Google internal coding
>>>> convention or
>>>> something, do we really want to enforce it rather than discuss
>>>> and decide what we actually want?
>>>>
>>>> With my limited C++ knowledge, the main distinction between struct and
>>>> class
>>>> when both can appear interchangeably is that struct defaults to public:
>>>
>>> The default applies to class members and base classes, but that's the
>>> *only* distinction.
>>>
>>>> and class defaults to private:, and I think it is best to use those that
>>>> way, rather than having tons of class ... { public: ... } everywhere.
>>>>
>>>> There are many C++ class boolean properties, rather than just
>>>> POD vs. non-POD and we could pick any of them instead of this
>>>> particular one
>>>> for the struct vs. class distinction if we wanted to enforce it, but why?
>>>
>>> I'm also unconvinced that POD is a useful distinction. A class might
>>> be a POD today, but then we decide to add a default constructor to it
>>> (or if/when we move to C++11, add default member initializers to it).
>>> Does that mean we need to replace struct with class to follow this
>>> convention?
>>>
>>> Or we might decide to add a std::string member to it, which stops it
>>> being a POD. Should every reference to it be changed from struct to
>>> class?
>>
>> Right, that's a downside of such a convention.  It can be mitigated
>> (but not avoided completely) by eschewing the class-key in references
>> to the type if it's unambiguous.  The warning suggests to drop
>> the class-key when it's possible.  I didn't follow that suggestion
>> in the cleanup patch only out of concern that people used to seeing
>> the 'class' or 'struct' there might be bothered by its removal.
>>
>> At the same time, adding a std::string member to A POD can have
>> serious ripple effects in a code base that assumes the struct is
>> a POD.  -Wclass-memaccess finds a subset of those but not nearly
>> all of them (I'd love to see a warning that detected more).
>>
>>> Personally I think "no user-defined constructors" might be a more
>>> useful distinction than POD. This is not a POD (because of the
>>> std::string member) but I don't see why it should be a class not a
>>> struct:
>>>
>>> struct A {
>>>    std::string name;
>>>    int id;
>>> };
>>
>> It looks like a fine struct to me :)
>>
>> But the answer depends on the goal of the convention.  If it is
>> to make it clear to users of A whether it can be used in a context
>> that expects a "POD" (such as some GCC containers) then A would
>> have to be a class.  Ideally, the POD requirement would be enforced
>> by smarter warnings and/or by type traits and static assertions but
>> not all projects make use of those (yet).
> 
> If a container has requirements of an element class, it should enforce
> them with STATIC_ASSERT.  Trying to express this distinction with
> 'struct' vs 'class' seems obscure and fragile; that's an attribute of
> a class that can't be checked by the actual code.

You're preaching to the choir here :)  See pr90904 for an example
where GCC's own auto_vec corrupts memory when used with a non-POD
type because it doesn't handle assignability correctly.  It doesn't
have a static assert to enforce it and I'm not sure it needs to
-- I think it should be fixed instead.

Richard's suggestion in the bug, one that I took, was to use vec
instead of auto_vec.  But vec itself is not safely assignable or
even copy constructible, so strictly speaking, it shouldn't be
used in containers that have that requirement (like hash_map).
The bug I ran into with hash_map then was pr90923 -- another
memory corruption that resulted in miscompilation due to hash_map
not being quire prepared to correctly handle non-POD types.

I'm sure these kinds of bugs aren't unique to GCC but are common
in any non-trivial code base that's migrating from C to C++.  And
that's where I think a convention like GCC's might be helpful.  I
suspect Lawrence may have introduced the POD struct guideline just
because he was anticipating these problems during GCC's migration
to C++.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
@ 2019-07-22 16:39   ` Martin Sebor
  2019-08-01 18:09     ` Jason Merrill
  2019-07-22 22:37   ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, " Jeff Law
  2019-07-24 19:30   ` Jeff Law
  2 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-22 16:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill

Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html

On 7/8/19 3:58 PM, Martin Sebor wrote:
> 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

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
  2019-07-22 16:39   ` Martin Sebor
@ 2019-07-22 22:37   ` Jeff Law
  2019-07-23  0:00     ` Mike Stump
  2019-07-23  1:34     ` Martin Sebor
  2019-07-24 19:30   ` Jeff Law
  2 siblings, 2 replies; 47+ messages in thread
From: Jeff Law @ 2019-07-22 22:37 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches

On 7/8/19 3:58 PM, Martin Sebor wrote:
> 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.
So there was some discussion on whether or not we even wanted to keep
the struct vs class convention for GCC.

Did that reach any kind of conclusion?  I don't have a strong opinion
here and will adjust to whatever the consensus is.

Jeff

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-22 22:37   ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, " Jeff Law
@ 2019-07-23  0:00     ` Mike Stump
  2019-07-23  1:34     ` Martin Sebor
  1 sibling, 0 replies; 47+ messages in thread
From: Mike Stump @ 2019-07-23  0:00 UTC (permalink / raw)
  To: Jeff Law; +Cc: Martin Sebor, gcc-patches

On Jul 22, 2019, at 3:19 PM, Jeff Law <law@redhat.com> wrote:
> 
> On 7/8/19 3:58 PM, Martin Sebor wrote:
>> 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.
> So there was some discussion on whether or not we even wanted to keep
> the struct vs class convention for GCC.
> 
> Did that reach any kind of conclusion?

Last I knew, the consensus was to accept patches that make the usage of struct and class consistent (no warning from clang by default, no defeat necessary).  I don't recall if it was settled if patches would be rejected merely because they they were not consistent.  I'd think we'd not reject patches on those grounds, but rather, let the people that build on clang style systems front the work, and contribute if they want.

But, I have a long memory, and the above might be a consensus or two ago.  :-)  I glanced around, and my google-fu turned up [1] and [2].

1 - https://gcc.gnu.org/PR61339
2 - https://gcc.gnu.org/wiki/FAQ#Wmismatched-tags

Kinda annoying to have a complaint for this, for no other reason other than a bad MS compiler.  It seems to have infected clang, and now in danger of infecting gcc, all for no good reason.  I'd rather MS remove the warning, clang remove the warning, and be done with the whole mess.  Oh well.  In 20 more years, no one will even recall why we had the warning and/or won't question it's existence.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-22 22:37   ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, " Jeff Law
  2019-07-23  0:00     ` Mike Stump
@ 2019-07-23  1:34     ` Martin Sebor
  2019-07-24 17:43       ` Jeff Law
  1 sibling, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-23  1:34 UTC (permalink / raw)
  To: Jeff Law, gcc-patches

On 7/22/19 4:19 PM, Jeff Law wrote:
> On 7/8/19 3:58 PM, Martin Sebor wrote:
>> 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.
> So there was some discussion on whether or not we even wanted to keep
> the struct vs class convention for GCC.
> 
> Did that reach any kind of conclusion?  I don't have a strong opinion
> here and will adjust to whatever the consensus is.

I'm not really sure how to gauge consensus here but this patch doesn't
actually enforce the GCC convention, it just makes it possible (none
of the new options is included in -Wall or -Wextra; they all have to
be enabled explicitly).

I myself wouldn't adopt a class/struct convention like that if given
the choice and wouldn't be at all upset if we dropped it going forward,
despite all the effort I put into the cleanup (which has already been
committed).  But us dropping it won't affect other projects that also
use it(*), albeit without enforcement, and so I would rather not tie
the consideration of the patch to the GCC guideline.  I would think
helping other projects enforce it if they find it helpful would be
seen as valuable even if we don't find the convention useful in GCC
anymore.

The struct/class convention aside, and ignoring the Visual C++ bug
that likely gave rise to it, the -Wmismatched-tags option is useful
for all projects that value consistency: declaring struct using
the same class-key everywhere, and same for classes.

Martin

PS The top-rated answer to the article below gives some idea of how
popular this struct/class rule of thumb might be out there:
   https://stackoverflow.com/questions/54585

Incidentally, among the many hits I get when searching for struct
vs class online are guidelines to prefer one over the other due
to better interoperability, efficiency, or expressiveness in some
languages that do make more of a distinction between the two than
C++ does (e.g., C#, D, Ruby, or Swift).  I can see mixed language
projects or users coming to C++ from those other languages wanting
to use a convention in C++ that reflects the use of the two keywords
in those other languages even if the use doesn't actually have
the same effect.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-08 21:58 [PATCH 0/3] add support for POD struct convention (PR 61339) Martin Sebor
                   ` (3 preceding siblings ...)
  2019-07-12  8:41 ` [PATCH 0/3] add support for POD struct convention " Jakub Jelinek
@ 2019-07-23 16:20 ` Arvind Sankar
  2019-07-23 16:42   ` Martin Sebor
  4 siblings, 1 reply; 47+ messages in thread
From: Arvind Sankar @ 2019-07-23 16:20 UTC (permalink / raw)
  To: gcc-patches

Hi, SVN rev 273311 appears to have been committed without regenerating
gcc/config*?

Thanks.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-23 16:20 ` Arvind Sankar
@ 2019-07-23 16:42   ` Martin Sebor
  2019-07-23 16:54     ` Arvind Sankar
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-07-23 16:42 UTC (permalink / raw)
  To: Arvind Sankar, gcc-patches

On 7/23/19 10:11 AM, Arvind Sankar wrote:
> Hi, SVN rev 273311 appears to have been committed without regenerating
> gcc/config*?

That commit wasn't meant to change the configure script since
the warning implementation isn't part of the patch.  Let me
back it out.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-23 16:42   ` Martin Sebor
@ 2019-07-23 16:54     ` Arvind Sankar
  0 siblings, 0 replies; 47+ messages in thread
From: Arvind Sankar @ 2019-07-23 16:54 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Arvind Sankar, gcc-patches

On Tue, Jul 23, 2019 at 10:31:16AM -0600, Martin Sebor wrote:
> On 7/23/19 10:11 AM, Arvind Sankar wrote:
> > Hi, SVN rev 273311 appears to have been committed without regenerating
> > gcc/config*?
> 
> That commit wasn't meant to change the configure script since
> the warning implementation isn't part of the patch.  Let me
> back it out.
> 
> Martin

Thanks

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-23  1:34     ` Martin Sebor
@ 2019-07-24 17:43       ` Jeff Law
  0 siblings, 0 replies; 47+ messages in thread
From: Jeff Law @ 2019-07-24 17:43 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches

On 7/22/19 6:53 PM, Martin Sebor wrote:
> On 7/22/19 4:19 PM, Jeff Law wrote:
>> On 7/8/19 3:58 PM, Martin Sebor wrote:
>>> 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.
>> So there was some discussion on whether or not we even wanted to keep
>> the struct vs class convention for GCC.
>>
>> Did that reach any kind of conclusion?  I don't have a strong opinion
>> here and will adjust to whatever the consensus is.
> 
> I'm not really sure how to gauge consensus here but this patch doesn't
> actually enforce the GCC convention, it just makes it possible (none
> of the new options is included in -Wall or -Wextra; they all have to
> be enabled explicitly).
I'm not entirely sure either.  To a large degree this feels like a case
where the C++ leads as well as the major contributors chime in.

How about this, when I get back from PTO if there haven't been
objections from the major contributors, then we go forward and change
the convention.  That givesfolks just shy of 2 weeks to object (I've
read Jason and Jon's comments as supporting dropping the existing
convention).  If there's objections, then we discuss further :-)


> 
> I myself wouldn't adopt a class/struct convention like that if given
> the choice and wouldn't be at all upset if we dropped it going forward,
> despite all the effort I put into the cleanup (which has already been
> committed).  But us dropping it won't affect other projects that also
> use it(*), albeit without enforcement, and so I would rather not tie
> the consideration of the patch to the GCC guideline.  I would think
> helping other projects enforce it if they find it helpful would be
> seen as valuable even if we don't find the convention useful in GCC
> anymore.
Right.  Given this seems to be a convention used elsewhere we could
still go forward with your patch to warn, even if we decide against
using the convention going forward for GCC.  It'd be your call if you
want to go forward with the warning patch.

Jeff

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
  2019-07-22 16:39   ` Martin Sebor
  2019-07-22 22:37   ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, " Jeff Law
@ 2019-07-24 19:30   ` Jeff Law
  2 siblings, 0 replies; 47+ messages in thread
From: Jeff Law @ 2019-07-24 19:30 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches

On 7/8/19 3:58 PM, Martin Sebor wrote:
> 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-61339-impl.diff
> 
> 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.
So I'm going to defer to Jason here given this is a front-end patch.
Your call whether or not to push on it prior to getting any resolution
on changing GCC's conventions.

jeff

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-07-22 16:39   ` Martin Sebor
@ 2019-08-01 18:09     ` Jason Merrill
  2019-08-01 23:35       ` Martin Sebor
  0 siblings, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-08-01 18:09 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches; +Cc: Jonathan Wakely

On 7/22/19 12:34 PM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
> 
> On 7/8/19 3:58 PM, Martin Sebor wrote:
>> 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.

As Jonathan and I were saying in the connected discussion, the *pod 
warnings enforce questionable coding guidelines, so I'd rather not have 
them in the compiler.

-Wmismatched-tags is useful to have, given the MSVC/clang situation, but 
I wonder about memory consumption from all the record keeping.  Do you 
have any overhead measurements?

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-08-01 18:09     ` Jason Merrill
@ 2019-08-01 23:35       ` Martin Sebor
  2019-08-05 19:25         ` Jason Merrill
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-08-01 23:35 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches; +Cc: Jonathan Wakely

On 8/1/19 12:09 PM, Jason Merrill wrote:
> On 7/22/19 12:34 PM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
>>
>> On 7/8/19 3:58 PM, Martin Sebor wrote:
>>> 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.
> 
> As Jonathan and I were saying in the connected discussion, the *pod 
> warnings enforce questionable coding guidelines, so I'd rather not have 
> them in the compiler.

What specifically do you consider questionable?

When defining a new class one has to decide whether to use 'struct'
and when to use 'class'.  Since there's no difference, adopting any
convention to help make a consistent choice seems like a natural
thing to do.  You could say (as I think someone in this thread did):
"make it 'class' when it has a ctor or dtor."  Someone else might
add: "or assignment."  Someone else still might go even further
and include "private members or bases or virtual functions."  Since
all the type definitions end up equivalent regardless of the class-key,
all these choices seem equally reasonable.  But all of them are also
arbitrary.  The only difference is that by being based on a common
(and at least historically, widely relied on) property, the POD
convention is less arbitrary.  That's probably why it's apparently
quite popular (as evident from the Stack Exchange thread).

> -Wmismatched-tags is useful to have, given the MSVC/clang situation, but 
> I wonder about memory consumption from all the record keeping.  Do you 
> have any overhead measurements?

I did think about the overhead but not knowing if the patch would
even be considered I didn't spend time optimizing it.

In an instrumented build of GCC with the patch I just did I have
collected the following stats for the number of elements in
the rec2loc hash table (for 998 files):

   rec2loc elements   locvec elements
   min:           0                 0
   max:       2,785             3,303
   mean:        537             1,043
   median:      526             1,080

The locvec data are based on totals for the whole hash table, so
in the worst case the hash_map has 2,785 elements, and the sum of
all locvec lengths in all those elements is 3,303.  Each rec2loc
element takes up 16 bytes, plus the size of the locvec data.

If my math is right (which doesn't happen too often) in the worst
case the bookkeeping overhead is 43KB for the hash_map plus on
the order of 140KB for the vectors (just doubling the number of
elements to account for capacity = 2 X size, times 24 for
the flag_func_loc_t element type).  So 183K in the worst case
in GCC.

There are a few ways to reduce that if it seems excessive.

One is by avoiding some waste in flag_func_loc_t which is

   pair<tag_types, pair<bool, pair<tree, location_t>>>

tree could come first and tag_types and the bool could share
space.  That should bring it down to 16 in LP64, for about
30% off the 183K.

Beyond that, we could change the algorithm to discard records
for prior declarations after the first definition has been seen
(and any mismatches diagnosed).

We could also only collect one record for each definition
in system headers rather than one for every declaration and
reference.

I'd have to play with it to see how much of a difference these
tweaks would make.

If we mainly care about memory usage when compiling GCC (as
opposed to third party code that would have to explicitly opt
in to the warning) then I'd expect to get by far the biggest
savings by cleaning up GCC code to get rid of the unnecessary
class-key.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-08-01 23:35       ` Martin Sebor
@ 2019-08-05 19:25         ` Jason Merrill
  2019-08-05 21:56           ` Martin Sebor
  0 siblings, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-08-05 19:25 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches; +Cc: Jonathan Wakely

On 8/1/19 7:35 PM, Martin Sebor wrote:
> On 8/1/19 12:09 PM, Jason Merrill wrote:
>> On 7/22/19 12:34 PM, Martin Sebor wrote:
>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
>>>
>>> On 7/8/19 3:58 PM, Martin Sebor wrote:
>>>> 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.
>>
>> As Jonathan and I were saying in the connected discussion, the *pod 
>> warnings enforce questionable coding guidelines, so I'd rather not 
>> have them in the compiler.
> 
> What specifically do you consider questionable?

> When defining a new class one has to decide whether to use 'struct'
> and when to use 'class'.  Since there's no difference, adopting any
> convention to help make a consistent choice seems like a natural
> thing to do.  You could say (as I think someone in this thread did):
> "make it 'class' when it has a ctor or dtor."  Someone else might
> add: "or assignment."  Someone else still might go even further
> and include "private members or bases or virtual functions."  Since
> all the type definitions end up equivalent regardless of the class-key,
> all these choices seem equally reasonable.  But all of them are also
> arbitrary.  The only difference is that by being based on a common
> (and at least historically, widely relied on) property, the POD
> convention is less arbitrary.  That's probably why it's apparently
> quite popular (as evident from the Stack Exchange thread).

Yes, all of them are somewhat arbitrary.  This one seems like a poor 
choice, for the reasons Jon and I gave in the other thread.  IMO 
'aggregate' would be a better property to use.

Which Stack Exchange thread do you mean?

>> -Wmismatched-tags is useful to have, given the MSVC/clang situation, 
>> but I wonder about memory consumption from all the record keeping.  Do 
>> you have any overhead measurements?
> 
> I did think about the overhead but not knowing if the patch would
> even be considered I didn't spend time optimizing it.
> 
> In an instrumented build of GCC with the patch I just did I have
> collected the following stats for the number of elements in
> the rec2loc hash table (for 998 files):
> 
>    rec2loc elements   locvec elements
>    min:           0                 0
>    max:       2,785             3,303
>    mean:        537             1,043
>    median:      526             1,080
> 
> The locvec data are based on totals for the whole hash table, so
> in the worst case the hash_map has 2,785 elements, and the sum of
> all locvec lengths in all those elements is 3,303.  Each rec2loc
> element takes up 16 bytes, plus the size of the locvec data.
> 
> If my math is right (which doesn't happen too often) in the worst
> case the bookkeeping overhead is 43KB for the hash_map plus on
> the order of 140KB for the vectors (just doubling the number of
> elements to account for capacity = 2 X size, times 24 for
> the flag_func_loc_t element type).  So 183K in the worst case
> in GCC.

183k cumulative over all the GCC sources doesn't sound excessive, but...

> There are a few ways to reduce that if it seems excessive.
> 
> One is by avoiding some waste in flag_func_loc_t which is
> 
>    pair<tag_types, pair<bool, pair<tree, location_t>>>
> 
> tree could come first and tag_types and the bool could share
> space.  That should bring it down to 16 in LP64, for about
> 30% off the 183K.
> 
> Beyond that, we could change the algorithm to discard records
> for prior declarations after the first definition has been seen
> (and any mismatches diagnosed).
> 
> We could also only collect one record for each definition
> in system headers rather than one for every declaration and
> reference.

...these all sound worthwhile.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-08-05 19:25         ` Jason Merrill
@ 2019-08-05 21:56           ` Martin Sebor
  2019-08-05 22:43             ` Jason Merrill
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-08-05 21:56 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches; +Cc: Jonathan Wakely

On 8/5/19 1:25 PM, Jason Merrill wrote:
> On 8/1/19 7:35 PM, Martin Sebor wrote:
>> On 8/1/19 12:09 PM, Jason Merrill wrote:
>>> On 7/22/19 12:34 PM, Martin Sebor wrote:
>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
>>>>
>>>> On 7/8/19 3:58 PM, Martin Sebor wrote:
>>>>> 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.
>>>
>>> As Jonathan and I were saying in the connected discussion, the *pod 
>>> warnings enforce questionable coding guidelines, so I'd rather not 
>>> have them in the compiler.
>>
>> What specifically do you consider questionable?
> 
>> When defining a new class one has to decide whether to use 'struct'
>> and when to use 'class'.  Since there's no difference, adopting any
>> convention to help make a consistent choice seems like a natural
>> thing to do.  You could say (as I think someone in this thread did):
>> "make it 'class' when it has a ctor or dtor."  Someone else might
>> add: "or assignment."  Someone else still might go even further
>> and include "private members or bases or virtual functions."  Since
>> all the type definitions end up equivalent regardless of the class-key,
>> all these choices seem equally reasonable.  But all of them are also
>> arbitrary.  The only difference is that by being based on a common
>> (and at least historically, widely relied on) property, the POD
>> convention is less arbitrary.  That's probably why it's apparently
>> quite popular (as evident from the Stack Exchange thread).
> 
> Yes, all of them are somewhat arbitrary.  This one seems like a poor 
> choice, for the reasons Jon and I gave in the other thread.  IMO 
> 'aggregate' would be a better property to use.
> 
> Which Stack Exchange thread do you mean?

The one I mentioned here:
   https://gcc.gnu.org/ml/gcc-patches/2019-07/msg01472.html

i.e. the most popular answer to the question

   When should you use a class vs a struct in C++?

is

   I would recommend using structs as plain-old-data structures
   without any class-like features, and using classes as aggregate
   data structures with private data and member functions.

   https://stackoverflow.com/questions/54585

My goal with the warning is to provide a way to help enforce
the convention we know is in use, both in GCC (at least until
it's removed) and in other projects.  I'm not interested in
inventing something new even if it's "better" (in some sense)
than the convention we know is in use.

That said, as the C++ standard itself says, PODs (or in recent
revisions, trivial standard-layout types) are most commonly
associated with interoperability with C (and other languages).
Aggregates can have data members of types that don't exist in
C (references, pointers to members).  So a convention that uses
the keyword 'struct' to reflect the common interoperability
aspect or that wants to convey that a C++ struct looks and feels
like one would expect of a C struct, will want to be based on
the concept of POD, not that of an aggregate.

>>> -Wmismatched-tags is useful to have, given the MSVC/clang situation, 
>>> but I wonder about memory consumption from all the record keeping.  
>>> Do you have any overhead measurements?
>>
>> I did think about the overhead but not knowing if the patch would
>> even be considered I didn't spend time optimizing it.
>>
>> In an instrumented build of GCC with the patch I just did I have
>> collected the following stats for the number of elements in
>> the rec2loc hash table (for 998 files):
>>
>>    rec2loc elements   locvec elements
>>    min:           0                 0
>>    max:       2,785             3,303
>>    mean:        537             1,043
>>    median:      526             1,080
>>
>> The locvec data are based on totals for the whole hash table, so
>> in the worst case the hash_map has 2,785 elements, and the sum of
>> all locvec lengths in all those elements is 3,303.  Each rec2loc
>> element takes up 16 bytes, plus the size of the locvec data.
>>
>> If my math is right (which doesn't happen too often) in the worst
>> case the bookkeeping overhead is 43KB for the hash_map plus on
>> the order of 140KB for the vectors (just doubling the number of
>> elements to account for capacity = 2 X size, times 24 for
>> the flag_func_loc_t element type).  So 183K in the worst case
>> in GCC.
> 
> 183k cumulative over all the GCC sources doesn't sound excessive, but...
> 
>> There are a few ways to reduce that if it seems excessive.
>>
>> One is by avoiding some waste in flag_func_loc_t which is
>>
>>    pair<tag_types, pair<bool, pair<tree, location_t>>>
>>
>> tree could come first and tag_types and the bool could share
>> space.  That should bring it down to 16 in LP64, for about
>> 30% off the 183K.
>>
>> Beyond that, we could change the algorithm to discard records
>> for prior declarations after the first definition has been seen
>> (and any mismatches diagnosed).
>>
>> We could also only collect one record for each definition
>> in system headers rather than one for every declaration and
>> reference.
> 
> ...these all sound worthwhile.

Okay, I'll look into it.

To be clear: it was 183K maximum for a single compilation, not
aggregate for all of them.

Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339)
  2019-08-05 21:56           ` Martin Sebor
@ 2019-08-05 22:43             ` Jason Merrill
  2019-12-03 21:49               ` [PATCH] add " Martin Sebor
  0 siblings, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-08-05 22:43 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches, Jonathan Wakely

On Mon, Aug 5, 2019 at 5:50 PM Martin Sebor <msebor@gmail.com> wrote:
>
> On 8/5/19 1:25 PM, Jason Merrill wrote:
> > On 8/1/19 7:35 PM, Martin Sebor wrote:
> >> On 8/1/19 12:09 PM, Jason Merrill wrote:
> >>> On 7/22/19 12:34 PM, Martin Sebor wrote:
> >>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
> >>>>
> >>>> On 7/8/19 3:58 PM, Martin Sebor wrote:
> >>>>> 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.
> >>>
> >>> As Jonathan and I were saying in the connected discussion, the *pod
> >>> warnings enforce questionable coding guidelines, so I'd rather not
> >>> have them in the compiler.
> >>
> >> What specifically do you consider questionable?
> >
> >> When defining a new class one has to decide whether to use 'struct'
> >> and when to use 'class'.  Since there's no difference, adopting any
> >> convention to help make a consistent choice seems like a natural
> >> thing to do.  You could say (as I think someone in this thread did):
> >> "make it 'class' when it has a ctor or dtor."  Someone else might
> >> add: "or assignment."  Someone else still might go even further
> >> and include "private members or bases or virtual functions."  Since
> >> all the type definitions end up equivalent regardless of the class-key,
> >> all these choices seem equally reasonable.  But all of them are also
> >> arbitrary.  The only difference is that by being based on a common
> >> (and at least historically, widely relied on) property, the POD
> >> convention is less arbitrary.  That's probably why it's apparently
> >> quite popular (as evident from the Stack Exchange thread).
> >
> > Yes, all of them are somewhat arbitrary.  This one seems like a poor
> > choice, for the reasons Jon and I gave in the other thread.  IMO
> > 'aggregate' would be a better property to use.
> >
> > Which Stack Exchange thread do you mean?
>
> The one I mentioned here:
>    https://gcc.gnu.org/ml/gcc-patches/2019-07/msg01472.html
>
> i.e. the most popular answer to the question
>
>    When should you use a class vs a struct in C++?
>
> is
>
>    I would recommend using structs as plain-old-data structures
>    without any class-like features, and using classes as aggregate
>    data structures with private data and member functions.
>
>    https://stackoverflow.com/questions/54585
>
> My goal with the warning is to provide a way to help enforce
> the convention we know is in use, both in GCC (at least until
> it's removed) and in other projects.  I'm not interested in
> inventing something new even if it's "better" (in some sense)
> than the convention we know is in use.

We don't know that this is in use as a strict division, anywhere.  All
the answers at that URL are using the term pretty loosely.  The top
answer says, "I would recommend using structs as plain-old-data
structures without any class-like features, and using classes as
aggregate data structures with private data and member functions."

A struct containing a std::string is not using any class-like
features, but is not a POD.  I oppose a coding convention that
requires the user to change that to a class with public: at the top.
And so I oppose introducing warnings to enforce such a convention.

> That said, as the C++ standard itself says, PODs (or in recent
> revisions, trivial standard-layout types) are most commonly
> associated with interoperability with C (and other languages).
> Aggregates can have data members of types that don't exist in
> C (references, pointers to members).  So a convention that uses
> the keyword 'struct' to reflect the common interoperability
> aspect or that wants to convey that a C++ struct looks and feels
> like one would expect of a C struct, will want to be based on
> the concept of POD, not that of an aggregate.

Note that the mention of pointers to members was removed from the POD
definition in C++03.  And then the term itself was deemed not a useful
category.

To me, the important C-like look and feel of a 'struct' is that it is
intended for users to access the data directly, rather than through
member functions.  A struct with a string qualifies.  It is possible
for a class to be intended for use that way but still not qualify as
an aggregate, e.g. because it has a constructor only to allow
parenthesized initialization (as I've seen in GCC), but aggregate is a
better approximation than POD for this distinction.

> >>> -Wmismatched-tags is useful to have, given the MSVC/clang situation,
> >>> but I wonder about memory consumption from all the record keeping.
> >>> Do you have any overhead measurements?
> >>
> >> I did think about the overhead but not knowing if the patch would
> >> even be considered I didn't spend time optimizing it.
> >>
> >> In an instrumented build of GCC with the patch I just did I have
> >> collected the following stats for the number of elements in
> >> the rec2loc hash table (for 998 files):
> >>
> >>    rec2loc elements   locvec elements
> >>    min:           0                 0
> >>    max:       2,785             3,303
> >>    mean:        537             1,043
> >>    median:      526             1,080
> >>
> >> The locvec data are based on totals for the whole hash table, so
> >> in the worst case the hash_map has 2,785 elements, and the sum of
> >> all locvec lengths in all those elements is 3,303.  Each rec2loc
> >> element takes up 16 bytes, plus the size of the locvec data.
> >>
> >> If my math is right (which doesn't happen too often) in the worst
> >> case the bookkeeping overhead is 43KB for the hash_map plus on
> >> the order of 140KB for the vectors (just doubling the number of
> >> elements to account for capacity = 2 X size, times 24 for
> >> the flag_func_loc_t element type).  So 183K in the worst case
> >> in GCC.
> >
> > 183k cumulative over all the GCC sources doesn't sound excessive, but...
> >
> >> There are a few ways to reduce that if it seems excessive.
> >>
> >> One is by avoiding some waste in flag_func_loc_t which is
> >>
> >>    pair<tag_types, pair<bool, pair<tree, location_t>>>
> >>
> >> tree could come first and tag_types and the bool could share
> >> space.  That should bring it down to 16 in LP64, for about
> >> 30% off the 183K.
> >>
> >> Beyond that, we could change the algorithm to discard records
> >> for prior declarations after the first definition has been seen
> >> (and any mismatches diagnosed).
> >>
> >> We could also only collect one record for each definition
> >> in system headers rather than one for every declaration and
> >> reference.
> >
> > ...these all sound worthwhile.
>
> Okay, I'll look into it.
>
> To be clear: it was 183K maximum for a single compilation, not
> aggregate for all of them.

Aha.  Thanks.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-07-12  8:41 ` [PATCH 0/3] add support for POD struct convention " Jakub Jelinek
  2019-07-12 11:44   ` Jonathan Wakely
@ 2019-08-14 18:50   ` Pedro Alves
  2019-08-19 19:15     ` Jason Merrill
  1 sibling, 1 reply; 47+ messages in thread
From: Pedro Alves @ 2019-08-14 18:50 UTC (permalink / raw)
  To: Jakub Jelinek, Martin Sebor, Jonathan Wakely, Jason Merrill,
	Nathan Sidwell, Richard Biener
  Cc: gcc-patches

On 7/12/19 9:24 AM, Jakub Jelinek wrote:
> I'd just arrange that when being compiled with clang we compile with
> -Wno-mismatched-tags to get rid of their misdesigned warning and not add
> such misdesigned warning to GCC, that will just help people spread this
> weirdo requirement further.

FWIW and FYI, this is what GDB does (gdb/warning.m4).

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH 0/3] add support for POD struct convention (PR 61339)
  2019-08-14 18:50   ` Pedro Alves
@ 2019-08-19 19:15     ` Jason Merrill
  0 siblings, 0 replies; 47+ messages in thread
From: Jason Merrill @ 2019-08-19 19:15 UTC (permalink / raw)
  To: Pedro Alves
  Cc: Jakub Jelinek, Martin Sebor, Jonathan Wakely, Nathan Sidwell,
	Richard Biener, gcc-patches

On Wed, Aug 14, 2019, 11:44 AM Pedro Alves <palves@redhat.com> wrote:

> On 7/12/19 9:24 AM, Jakub Jelinek wrote:
> > I'd just arrange that when being compiled with clang we compile with
> > -Wno-mismatched-tags to get rid of their misdesigned warning and not add
> > such misdesigned warning to GCC, that will just help people spread this
> > weirdo requirement further.
>
> FWIW and FYI, this is what GDB does (gdb/warning.m4).
>

As I understand it, the warning reflects a hard requirement for code that
wants to be portable to MSVC++, so I think it's useful to have for that
purpose.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH] add -Wmismatched-tags (PR 61339)
  2019-08-05 22:43             ` Jason Merrill
@ 2019-12-03 21:49               ` Martin Sebor
  2019-12-04 23:37                 ` Jason Merrill
  2020-02-18  8:42                 ` Stephan Bergmann
  0 siblings, 2 replies; 47+ messages in thread
From: Martin Sebor @ 2019-12-03 21:49 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Jonathan Wakely

[-- Attachment #1: Type: text/plain, Size: 3967 bytes --]

On 8/5/19 4:30 PM, Jason Merrill wrote:
> On Mon, Aug 5, 2019 at 5:50 PM Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 8/5/19 1:25 PM, Jason Merrill wrote:
>>> On 8/1/19 7:35 PM, Martin Sebor wrote:
>>>> On 8/1/19 12:09 PM, Jason Merrill wrote:
>>>>> On 7/22/19 12:34 PM, Martin Sebor wrote:
>>>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
...
>>>>> -Wmismatched-tags is useful to have, given the MSVC/clang situation,
>>>>> but I wonder about memory consumption from all the record keeping.
>>>>> Do you have any overhead measurements?
>>>>
>>>> I did think about the overhead but not knowing if the patch would
>>>> even be considered I didn't spend time optimizing it.
>>>>
>>>> In an instrumented build of GCC with the patch I just did I have
>>>> collected the following stats for the number of elements in
>>>> the rec2loc hash table (for 998 files):
>>>>
>>>>     rec2loc elements   locvec elements
>>>>     min:           0                 0
>>>>     max:       2,785             3,303
>>>>     mean:        537             1,043
>>>>     median:      526             1,080
>>>>
>>>> The locvec data are based on totals for the whole hash table, so
>>>> in the worst case the hash_map has 2,785 elements, and the sum of
>>>> all locvec lengths in all those elements is 3,303.  Each rec2loc
>>>> element takes up 16 bytes, plus the size of the locvec data.
>>>>
>>>> If my math is right (which doesn't happen too often) in the worst
>>>> case the bookkeeping overhead is 43KB for the hash_map plus on
>>>> the order of 140KB for the vectors (just doubling the number of
>>>> elements to account for capacity = 2 X size, times 24 for
>>>> the flag_func_loc_t element type).  So 183K in the worst case
>>>> in GCC.
>>>
>>> 183k cumulative over all the GCC sources doesn't sound excessive, but...
>>>
>>>> There are a few ways to reduce that if it seems excessive.
>>>>
>>>> One is by avoiding some waste in flag_func_loc_t which is
>>>>
>>>>     pair<tag_types, pair<bool, pair<tree, location_t>>>
>>>>
>>>> tree could come first and tag_types and the bool could share
>>>> space.  That should bring it down to 16 in LP64, for about
>>>> 30% off the 183K.
>>>>
>>>> Beyond that, we could change the algorithm to discard records
>>>> for prior declarations after the first definition has been seen
>>>> (and any mismatches diagnosed).
>>>>
>>>> We could also only collect one record for each definition
>>>> in system headers rather than one for every declaration and
>>>> reference.
>>>
>>> ...these all sound worthwhile.
>>
>> Okay, I'll look into it.
>>
>> To be clear: it was 183K maximum for a single compilation, not
>> aggregate for all of them.
> 
> Aha.  Thanks.

Attached is a revised patch that implements just -Wmismatched-tags
without the other two warnings (-Wclass-not-pod and -Wstruct-is-pod).
It also implements the optimizations mentioned above.  To make it
easier to do the tag cleanup by simply dropping the class-key when
it isn't necessary (suggested during the review of the cleanup patch)
I added another warning: -Wredundant-tags to point out instances
where the class-key or enum-key can safely be dropped. Both warnings
are off by default.

With the optimizations in place, the biggest space overhead of
using the option I measured in a GCC build was 990 elements of
the record_to_locs_t hash table, plus 2756 elements of the locvec
vector.  In LP64, each record_to_locs_t element type is 16 bytes
and each element of the locvec vector is 24 bytes, so the maximum
space overhead is on the order of 80K.  The average overhead per
GCC translation unit was about 30K.

The patch depends on fixes for a few bugs in GCC hash_tables (PR
92761 and 92762).  I will post those separately.

Martin

PS Independently of this patch I will propose updating the GCC
Coding Conventions to remove the guideline to use the struct
class-key for PODs and class for non-PODs:
   https://gcc.gnu.org/codingconventions.html#Class_Use

[-- Attachment #2: gcc-61339.diff --]
[-- Type: text/x-patch, Size: 36598 bytes --]

PR c++/61339 - add warning for mismatch between struct and class

gcc/c-family/ChangeLog:

	PR c++/61339
	* c.opt (-Wmismatched-tags, -Wredundant-tags): New options.

gcc/cp/ChangeLog:

	PR c++/61339
	* parser.c (cp_parser_maybe_warn_enum_key): New function.
	(rec_decl_loc_t): New class.
	(record_to_locs_t): New typedef.
	(rec2loc): New global variable.
	(cp_parser_elaborated_type_specifier): Call it.
	(cp_parser_class_head): Call cp_parser_check_class_key.
	(cp_parser_check_class_key): Add arguments.
	(diag_mismatched_tags): New function.
	(c_parse_file): Call diag_mismatched_tags.

gcc/testsuite/ChangeLog:

	PR c++/61339
	* g++.dg/warn/Wmismatched-tags.C: New test.
	* g++.dg/warn/Wredundant-tags.C: New test.

gcc/ChangeLog:

	PR c++/61339
	* doc/invoke.texi (-Wmismatched-tags, -Wredundant-tags): Document
	new C++ options.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 914a2f0ef44..4f3d3cf0d43 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -755,6 +755,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 or referenced 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.
@@ -783,6 +787,10 @@ Wpacked-not-aligned
 C ObjC C++ ObjC++ Var(warn_packed_not_aligned) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn when fields in a struct with the packed attribute are misaligned.
 
+Wredundant-tags
+C++ Objc++ Var(warn_redundant_tags) Warning
+Warn when a class or enumerated type is referenced using a redundant class-key.
+
 Wsized-deallocation
 C++ ObjC++ Var(warn_sized_deallocation) Warning EnabledBy(Wextra)
 Warn about missing sized deallocation functions.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index fb030022627..da9aa42e7a8 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2599,8 +2599,9 @@ static enum tag_types cp_parser_token_is_class_key
   (cp_token *);
 static enum tag_types cp_parser_token_is_type_parameter_key
   (cp_token *);
+static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid);
 static void cp_parser_check_class_key
-  (enum tag_types, tree type);
+(cp_parser *, location_t, enum tag_types, tree type, bool, bool);
 static void cp_parser_check_access_in_redeclaration
   (tree type, location_t location);
 static bool cp_parser_optional_template_keyword
@@ -18480,6 +18481,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   tree globalscope;
   cp_token *token = NULL;
 
+  /* For class and enum types the location of the class-key or enum-key.  */
+  location_t key_loc = cp_lexer_peek_token (parser->lexer)->location;
+  /* For a scoped enum, the 'class' or 'struct' keyword id.  */
+  rid scoped_key = RID_MAX;
+
   /* See if we're looking at the `enum' keyword.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM))
     {
@@ -18490,10 +18496,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
       /* Issue a warning if the `struct' or `class' key (for C++0x scoped
 	 enums) is used here.  */
       cp_token *token = cp_lexer_peek_token (parser->lexer);
-      if (cp_parser_is_keyword (token, RID_CLASS)
-	  || cp_parser_is_keyword (token, RID_STRUCT))
+      if (cp_parser_is_keyword (token, scoped_key = RID_CLASS)
+	  || cp_parser_is_keyword (token, scoped_key = RID_STRUCT))
 	{
-	  gcc_rich_location richloc (token->location);
+	  location_t loc = token->location;
+	  gcc_rich_location richloc (loc);
 	  richloc.add_range (input_location);
 	  richloc.add_fixit_remove ();
 	  pedwarn (&richloc, 0, "elaborated-type-specifier for "
@@ -18501,7 +18508,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		   token->u.value);
 	  /* Consume the `struct' or `class' and parse it anyway.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  /* Create a combined location for the whole scoped-enum-key.  */
+	  key_loc = make_location (key_loc, key_loc, loc);
 	}
+      else
+	scoped_key = RID_MAX;
+
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
     }
@@ -18517,6 +18529,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   /* Otherwise it must be a class-key.  */
   else
     {
+      key_loc = cp_lexer_peek_token (parser->lexer)->location;
       tag_type = cp_parser_class_key (parser);
       if (tag_type == none_type)
 	return error_mark_node;
@@ -18827,13 +18840,18 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		 "attributes ignored on elaborated-type-specifier that is not a forward declaration");
     }
 
-  if (tag_type != enum_type)
+  if (tag_type == enum_type)
+    cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
+  else
     {
+      /* Diagnose class/struct/union mismatches.  */
+      cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
+				 cp_parser_declares_only_class_p (parser));
+
       /* 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
@@ -24371,11 +24389,14 @@ cp_parser_class_head (cp_parser* parser,
 		       parser->num_template_parameter_lists);
     }
 
+  /* Diagnose class/struct/union mismatches.  */
+  cp_parser_check_class_key (parser, UNKNOWN_LOCATION, class_key, type,
+			     true, 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:
@@ -30598,14 +30619,154 @@ cp_parser_token_is_type_parameter_key (cp_token* token)
     }
 }
 
-/* Issue an error message if the CLASS_KEY does not match the TYPE.  */
+/* Diagnose redundant enum-keys.  */
+
+static void
+cp_parser_maybe_warn_enum_key (cp_parser *parser, location_t key_loc,
+			       tree type, rid scoped_key)
+{
+  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);
+
+  /* The enum-key is redundant for uses of the TYPE that are not
+     declarations and for which name lookup returns just the type
+     itself.  */
+  if (decl == type_decl)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant enum-key %<enum%s%> in reference to %q#T",
+		  (scoped_key == RID_CLASS ? " class"
+		   : scoped_key == RID_STRUCT ? " struct" : ""), 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);
+  }
+
+  /* Constructs 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 an ambiguity with
+     another symbol with the same name.
+     DEF_P is true for a class 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);
+    class_key_loc_t ckl (current_function_decl, input_location,
+			 class_key, key_redundant);
+    locvec.quick_push (ckl);
+  }
+
+  /* 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)
+  {
+    if (this == &rhs)
+      return *this;
+    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].func;
+  }
+
+  location_t location (unsigned i) const
+  {
+    return locvec[i].loc;
+  }
+
+  bool key_redundant (unsigned i) const
+  {
+    return locvec[i].key_redundant;
+  }
+
+  tag_types class_key (unsigned i) const
+  {
+    return locvec[i].class_key;
+  }
+
+  /* The location of a single mention of a class type with the given
+     class-key.  */
+  struct class_key_loc_t
+  {
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+
+    /* The function the type is mentioned in.  */
+    tree func;
+    /* The exact location.  */
+    location_t loc;
+    /* The class-key used in the mention of the type.  */
+    tag_types class_key;
+    /* True when the class-key could be omitted at this location
+       without an ambiguity with another symbol of the same name.  */
+    bool key_redundant;
+  };
+  /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
+  vec <class_key_loc_t> locvec;
+  /* LOCVEC index of the definition or UINT_MAX 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 for a class and the rec_decl_loc_t
+   description above.  */
+typedef hash_map<tree, rec_decl_loc_t> record_to_locs_t;
+static GTY (()) record_to_locs_t *rec2loc;
 
 static void
-cp_parser_check_class_key (enum tag_types class_key, tree type)
+diag_mismatched_tags (tree, rec_decl_loc_t &);
+
+/* Issue an error message if the CLASS_KEY does not match the TYPE.
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a declaration of class TYPE and clear for a reference to
+   it that is not a declaration of it.  */
+
+static void
+cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
+			   tag_types class_key, tree type, bool def_p,
+			   bool decl_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"
@@ -30613,7 +30774,212 @@ 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 (!warn_mismatched_tags && !warn_redundant_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);
+
+  /* The class-key is redundant for uses of the CLASS_TYPE that are
+     neither definitions of it nor declarations, and for which name
+     lookup returns just the type itself.  */
+  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		"redundant class-key %qs in reference to %q#T",
+		class_key == union_type ? "union"
+		: class_key == record_type ? "struct" : "class",
+		type);
+    }
+
+  if (seen_as_union || !warn_mismatched_tags)
+    return;
+
+  if (!rec2loc)
+    rec2loc = new record_to_locs_t ();
+
+  rec_decl_loc_t *rdl = rec2loc->get (type_decl);
+  if (!rdl)
+    {
+      /* TYPE_DECL is the first declaration of the type.  Just create
+	 a new entry for it.  */
+      rec2loc->put (type_decl, rec_decl_loc_t (class_key, false, def_p));
+      return;
+    }
+
+  /* A prior declaration of TYPE_DECL has been seen.  */
+
+  if (rdl->idxdef < UINT_MAX && rdl->def_class_key == class_key)
+    /* Do nothing if the class-key in this declaration matches
+       the definition.  */
+    return;
+
+  /* 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 ();
+
+  /* Append a record of this declaration to the vector.  */
+  rec_decl_loc_t::class_key_loc_t
+    ckl (current_function_decl, input_location,
+	 class_key, key_redundant);
+  rdl->locvec.safe_push (ckl);
+
+  if (rdl->idxdef < UINT_MAX)
+    {
+      /* As a space optimization diagnose declarations of a class
+	 whose definition has been seen and purge the LOCVEC of
+	 all entries except the definition.  */
+      diag_mismatched_tags (decl, *rdl);
+      if (rdl->idxdef)
+	{
+	  rec_decl_loc_t::class_key_loc_t ent = rdl->locvec[rdl->idxdef];
+	  rdl->locvec.release ();
+	  rdl->locvec.reserve (2);
+	  rdl->locvec.safe_push (ent);
+	  rdl->idxdef = 0;
+	}
+      else
+	/* Pop the entry pushed above for this declaration.  */
+	rdl->locvec.pop ();
+    }
+}
+
+/* Issues -Wmismatched-tags for a single class described by RECLOC.  */
+
+static void
+diag_mismatched_tags (tree type_decl, rec_decl_loc_t &recloc)
+{
+  unsigned ndecls = recloc.locvec.length ();
+
+  /* Skip a declaration that consistently uses the same class-key
+     or one with just a solitary declaration (i.e., TYPE_DECL). */
+  if (recloc.def_class_key != none_type || ndecls < 2)
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
+  /* Set if a class definition for RECLOC has been seen.  */
+  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))
+    if (++idx == ndecls)
+      return;
+
+  /* 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 xpect_key = recloc.class_key (def_p ? idxguide : 0);
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+  /* Set the function declaration to print in diagnostic context.  */
+  current_function_decl = recloc.function (idx);
+
+  location_t loc = recloc.location (idx);
+  bool key_redundant = recloc.key_redundant (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 (loc, OPT_Wmismatched_tags,
+	      "%qT declared with a mismatched class-key %qs",
+	      type_decl, xmatchkstr);
+
+  /* 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")),
+	  xpectkstr);
+
+  /* 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, xpectkstr);
+
+  /* Issue warnings for the remaining inconsistent 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 == xpect_key)
+	continue;
+
+      loc = recloc.location (i);
+      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,
+		  "%qT declared with a mismatched class-key %qs",
+		  type_decl, xmatchkstr);
+      /* 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")),
+	      xpectkstr);
     }
+
+  /* Restore the current function in case it was replaced above.  */
+  current_function_decl = save_func;
+}
+
+/* Issues -Wmismatched-tags for all classes.  Called at the end
+   of processing a translation unit, after declarations of all class
+   types and their uses have been recorded.  */
+
+static void
+diag_mismatched_tags ()
+{
+  /* REC2LOC should be empty if -Wmismatched-tags is disabled.  */
+  gcc_assert (warn_mismatched_tags || !rec2loc || rec2loc->is_empty ());
+  if (!rec2loc)
+    return;
+
+  /* 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)
+    {
+      tree type_decl = (*it).first;
+      rec_decl_loc_t &recloc = (*it).second;
+      diag_mismatched_tags (type_decl, recloc);
+    }
+
+  rec2loc->empty ();
+  delete rec2loc;
+  rec2loc = NULL;
+  /* Restore the current function.  */
+  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
@@ -43046,6 +43412,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 d165f31a865..1cd6b7ca8ad 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -236,7 +236,7 @@ in the following sections.
 -Wliteral-suffix @gol
 -Wmultiple-inheritance  -Wno-init-list-lifetime @gol
 -Wnamespaces  -Wnarrowing @gol
--Wpessimizing-move  -Wredundant-move @gol
+-Wpessimizing-move  -Wredundant-move -Wredundant-tags @gol
 -Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
 -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
@@ -3323,6 +3323,21 @@ treats the return value as if it were designated by an rvalue.
 
 This warning is enabled by @option{-Wextra}.
 
+@item -Wredundant-tags @r{(C++ and Objective-C++ only)}
+@opindex Wredundant-tags
+@opindex Wno-redundant-tags
+Warn about redundant class-key and enum-key in references to class types
+and enumerated types in contexts where the key can be eliminated without
+causing an ambiguity.  For example
+
+@smallexample
+struct foo;
+struct foo *p;   // -Wredundant-tags, keyword struct can be eliminated
+
+void foo ();   // "hides" struct foo
+void bar (struct foo&);   // no warning, keyword struct cannot be eliminated
+@end smallexample
+
 @item -fext-numeric-literals @r{(C++ and Objective-C++ only)}
 @opindex fext-numeric-literals
 @opindex fno-ext-numeric-literals
@@ -3458,6 +3473,29 @@ 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 option @option{-Wredundant-tags}.
+
 @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)}
 @opindex Wmultiple-inheritance
 @opindex Wno-multiple-inheritance
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..af827ff5cec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
@@ -0,0 +1,278 @@
+/* PR c++/61339 - add mismatch between struct and class
+   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" } */
+
+namespace Classes
+{
+class A;
+class A;
+
+struct B;
+struct B;
+
+union C;
+union C;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key 'struct'" }
+class D { };                // { dg-message "Classes::D' defined as 'class' here" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key 'class'" }
+struct E { };               // { dg-message "Classes::E' defined as 'struct' here" }
+
+class D;
+struct E;
+
+class D;
+struct E;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key" }
+
+class F;                    // { dg-message "Classes::F' first declared as 'class' here" }
+class F;
+
+struct G { };               // { dg-message "Classes::G' defined as 'struct' here" }
+}   // namespace Classes
+
+
+namespace Classes
+{
+class A;
+struct B;
+union C;
+class D;
+struct E;
+
+struct F;                   // { dg-warning "Classes::F' declared with a mismatched class-key" }
+
+struct G;
+}
+
+// Verify that the correct hint is provided, one to remove the class-key
+// when it's redundant, and one to (only) replace it with the correct one
+// when it's needed to disambiguate the reference to the class type.
+namespace RemoveOrReplace
+{
+struct Func;
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to remove" { target *-*-* } .-1 }
+
+void Func ();
+
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to replace" { target *-*-* } .-1 }
+
+class Var;
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to remove" { target *-*-* } .-1 }
+void f (struct Var*);       // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+int Var;
+
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to replace" { target *-*-* } .-1 }
+}
+
+namespace GlobalObjects
+{
+class A;                    // { dg-message "'GlobalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'GlobalObjects::B' first declared as 'struct' here" }
+class C { };                // { dg-message "'GlobalObjects::C' defined as 'class' here" }
+
+extern A a0;
+extern class A a1;
+extern class A a2;
+
+extern B b0;
+extern struct B b1;
+extern struct B b2;
+
+extern struct A a3;         // { dg-warning "GlobalObjects::A' declared with a mismatched class-key" }
+extern class A a4;
+
+extern class B b3;          // { dg-warning "GlobalObjects::B' declared with a mismatched class-key" }
+extern struct B b4;
+
+extern struct C c[];        // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+extern char
+arr[sizeof (struct C)];     // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+}   // namespace GlobalObjects
+
+
+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 (A*, B&)
+{
+  class A *a1;
+  class A *a2;
+
+  struct B *b1;
+  struct B *b2;
+
+  struct A *a3;             // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+  class A *a4;
+
+  class B *b3;              // { dg-warning "LocalObjects::B' declared with a mismatched class-key" }
+  struct B *b4;
+}
+
+void g (struct A*);         // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+
+}   // namespace LocalObjects
+
+
+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 "MemberClasses::G::H' declared with a mismatched class-key" }
+  class I { };              // { dg-message "MemberClasses::G::I' defined as 'class' here" }
+  struct I;                 // { dg-warning "MemberClasses::G::I' declared with a mismatched class-key" }
+};
+}   // namespace MemberClasses
+
+
+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 "DataMembers::G' declared with a mismatched class-key" }
+  class G *p1;
+
+  struct H &r0;
+  class H &r1;              // { dg-warning "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 "DataMembers::I::K' declared with a mismatched class-key" }
+
+  struct J j1;              // { dg-warning "DataMembers::I::J' declared with a mismatched class-key" }
+  struct K k1;
+};
+}   // namespace DataMembers
+
+
+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 "Templates::D' declared with a mismatched class-key" }
+template <int>
+class D                     // { dg-message "Templates::D' defined as 'class' here" }
+{ public: D (); };
+
+template <int> class E;     // { dg-warning "Templates::E' declared with a mismatched class-key" }
+template <int>
+struct E                    // { dg-message "Templates::E' defined as 'struct' here" }
+{ int i; };
+
+template <int> class D;
+template <int> struct E;
+
+template <int>
+struct D;                   // { dg-warning "Templates::D' declared with a mismatched class-key" }
+                            // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 }
+}   // namespace Templates
+
+
+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 "ExplicitSpecializations::D' declared with a mismatched class-key " }
+template <>
+struct D<0> { };            // { dg-message "ExplicitSpecializations::D' defined as 'struct' here" }
+
+template <int> struct E;
+template <> struct E<0>;    // { dg-warning "ExplicitSpecializations::E' declared with a mismatched class-key" }
+template <>
+class E<0> { };             // { dg-message "ExplicitSpecializations::E' defined as 'class' here" }
+
+template <int> struct F;
+template <> class F<0> { }; // { dg-message "ExplicitSpecializations::F' defined as 'class' here" }
+
+template <>
+struct F<0>;                // { dg-warning "ExplicitSpecializations::F' declared with a mismatched class-key" }
+}   // namespace ExplicitSpecializations
+
+
+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 "PartialSpecializations::E<const T>' first declared as 'struct' here" }
+
+template <class T>
+class E<const T>;           // { dg-warning "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 "PartialSpecializations::F<const T>' declared with a mismatched class-key" }
+}   // namespace PartialSpecializations
+
+
+namespace Classes
+{
+struct G;
+
+class G;                    // { dg-warning "Classes::G' declared with a mismatched class-key 'class'" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
new file mode 100644
index 00000000000..ac5afa912a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
@@ -0,0 +1,128 @@
+/* PR c++/61339 - add mismatch between struct and class
+   Test to verify that -Wredundant-tags is issued for references to class
+   types that use the class-key even though they don't need to.
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+struct A;
+
+extern A *pa;
+extern struct A *pa;        // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+extern A aa[];
+extern struct A aa[];       // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+void func (A*);
+void func (struct A*);      // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+int A;
+
+extern struct A *pa;
+extern struct A aa[];
+void func (struct A*);
+
+
+class B;
+
+extern B *pb;
+extern class B *pb;         // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+extern B ab[];
+extern class B ab[];        // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+void func (B*);
+void func (class B*);       // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+int B;
+
+extern class B *pb;
+extern class B ab[];
+void func (class B*);
+
+
+enum C { c0 };
+
+extern C *pc;
+extern enum C *pc;          // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+extern C ac[];
+extern enum C ac[];         // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+void func (C*);
+void func (enum C*);        // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+int C;
+
+extern enum C *pc;
+extern enum C ac[];
+void func (enum C*);
+
+
+#if __cplusplus > 199711L
+
+enum class D1 { d1 };
+enum struct D2 { d2 };
+
+#else
+
+enum D1 { d1 };
+enum D2 { d2 };
+
+#endif
+
+extern D1 *pd1;
+extern D2 *pd2;
+extern enum D1 *pd1;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } .-1 }
+
+extern enum D2 *pd2;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D2'" "C++ 98" { target c++98_only } .-1 }
+
+extern D1 ad1[];
+extern D2 ad2[];
+
+#if __cplusplus > 199711L
+extern enum class D1 ad1[]; // { dg-warning "redundant enum-key 'enum class' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'class' keyword" "C++ 11 and above" { target c++11 } .-1 }
+/* The pretty printer cannot differentiate between enum class and enum struct
+   because the C++ front-end doesn't encode it so allow for both in the text
+   of the warning below.  */
+extern enum struct D2 ad2[]; // { dg-warning "redundant enum-key 'enum struct' in reference to 'enum \(class|struct\) D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'struct' keyword" "C++ 11 and above" { target c++11 } .-1 }
+#else
+extern enum D1 ad1[];       // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } }
+#endif
+
+void func (D1*);
+void func (enum D1*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+void func (D2*);
+void func (enum D2*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+int D1, D2;
+
+extern enum D1 *pd1;
+extern enum D1 ad1[];
+void func (enum D1*);
+
+extern enum D2 *pd2;
+extern enum D2 ad2[];
+void func (enum D2*);
+
+
+union U;
+
+extern U *pu;
+extern union U *pu;         // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+extern U au[];
+extern union U au[];        // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+void func (U*);
+void func (union U*);       // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+int U;
+
+extern union U *pu;
+extern union U au[];
+void func (union U*);

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-03 21:49               ` [PATCH] add " Martin Sebor
@ 2019-12-04 23:37                 ` Jason Merrill
  2019-12-05 23:33                   ` Martin Sebor
  2020-02-18  8:42                 ` Stephan Bergmann
  1 sibling, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-12-04 23:37 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches, Jonathan Wakely

On 12/3/19 4:49 PM, Martin Sebor wrote:
> On 8/5/19 4:30 PM, Jason Merrill wrote:
>> On Mon, Aug 5, 2019 at 5:50 PM Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 8/5/19 1:25 PM, Jason Merrill wrote:
>>>> On 8/1/19 7:35 PM, Martin Sebor wrote:
>>>>> On 8/1/19 12:09 PM, Jason Merrill wrote:
>>>>>> On 7/22/19 12:34 PM, Martin Sebor wrote:
>>>>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
> ...
>>>>>> -Wmismatched-tags is useful to have, given the MSVC/clang situation,
>>>>>> but I wonder about memory consumption from all the record keeping.
>>>>>> Do you have any overhead measurements?
>>>>>
>>>>> I did think about the overhead but not knowing if the patch would
>>>>> even be considered I didn't spend time optimizing it.
>>>>>
>>>>> In an instrumented build of GCC with the patch I just did I have
>>>>> collected the following stats for the number of elements in
>>>>> the rec2loc hash table (for 998 files):
>>>>>
>>>>>     rec2loc elements   locvec elements
>>>>>     min:           0                 0
>>>>>     max:       2,785             3,303
>>>>>     mean:        537             1,043
>>>>>     median:      526             1,080
>>>>>
>>>>> The locvec data are based on totals for the whole hash table, so
>>>>> in the worst case the hash_map has 2,785 elements, and the sum of
>>>>> all locvec lengths in all those elements is 3,303.  Each rec2loc
>>>>> element takes up 16 bytes, plus the size of the locvec data.
>>>>>
>>>>> If my math is right (which doesn't happen too often) in the worst
>>>>> case the bookkeeping overhead is 43KB for the hash_map plus on
>>>>> the order of 140KB for the vectors (just doubling the number of
>>>>> elements to account for capacity = 2 X size, times 24 for
>>>>> the flag_func_loc_t element type).  So 183K in the worst case
>>>>> in GCC.
>>>>
>>>> 183k cumulative over all the GCC sources doesn't sound excessive, 
>>>> but...
>>>>
>>>>> There are a few ways to reduce that if it seems excessive.
>>>>>
>>>>> One is by avoiding some waste in flag_func_loc_t which is
>>>>>
>>>>>     pair<tag_types, pair<bool, pair<tree, location_t>>>
>>>>>
>>>>> tree could come first and tag_types and the bool could share
>>>>> space.  That should bring it down to 16 in LP64, for about
>>>>> 30% off the 183K.
>>>>>
>>>>> Beyond that, we could change the algorithm to discard records
>>>>> for prior declarations after the first definition has been seen
>>>>> (and any mismatches diagnosed).
>>>>>
>>>>> We could also only collect one record for each definition
>>>>> in system headers rather than one for every declaration and
>>>>> reference.
>>>>
>>>> ...these all sound worthwhile.
>>>
>>> Okay, I'll look into it.
>>>
>>> To be clear: it was 183K maximum for a single compilation, not
>>> aggregate for all of them.
>>
>> Aha.  Thanks.
> 
> Attached is a revised patch that implements just -Wmismatched-tags
> without the other two warnings (-Wclass-not-pod and -Wstruct-is-pod).
> It also implements the optimizations mentioned above.  To make it
> easier to do the tag cleanup by simply dropping the class-key when
> it isn't necessary (suggested during the review of the cleanup patch)
> I added another warning: -Wredundant-tags to point out instances
> where the class-key or enum-key can safely be dropped. Both warnings
> are off by default.
> 
> With the optimizations in place, the biggest space overhead of
> using the option I measured in a GCC build was 990 elements of
> the record_to_locs_t hash table, plus 2756 elements of the locvec
> vector.  In LP64, each record_to_locs_t element type is 16 bytes
> and each element of the locvec vector is 24 bytes, so the maximum
> space overhead is on the order of 80K.  The average overhead per
> GCC translation unit was about 30K.
> 
> The patch depends on fixes for a few bugs in GCC hash_tables (PR
> 92761 and 92762).  I will post those separately.
> 
> Martin
> 
> PS Independently of this patch I will propose updating the GCC
> Coding Conventions to remove the guideline to use the struct
> class-key for PODs and class for non-PODs:
>    https://gcc.gnu.org/codingconventions.html#Class_Use

> +class rec_decl_loc_t

Let's use "class" instead of "record" generally in this patch.

> +/* A mapping between a TYPE_DECL for a class and the rec_decl_loc_t
> +   description above.  */
> +typedef hash_map<tree, rec_decl_loc_t> record_to_locs_t;

You might want to use hash_map<tree_decl_hash, rec_decl_loc_t> so you 
get the decl-specific hash function rather than the generic pointer hash.

It's hard to distinguish between this type and the previous one by name; 
this one should probably have "map" in its name.

> +static GTY (()) record_to_locs_t *rec2loc;
...
> +    rec2loc = new record_to_locs_t ();

If this isn't GC-allocated, marking it with GTY(()) seems wrong.  How do 
you imagine this warning interacting with PCH?

> +  /* A prior declaration of TYPE_DECL has been seen.  */

The rest of this function from this point on seems like it would be 
better as a member function of rec_decl_loc_t.

> +static void
> +diag_mismatched_tags (tree type_decl, rec_decl_loc_t &recloc)

This could also be a member function.

> +  if (rdl->idxdef < UINT_MAX && rdl->def_class_key == class_key)

Maybe != rather than < ?

> +@item -Wmismatched-tags @r{(C++ and Objective-C++ only)}

The documentation of this flag should mention that the main reason 
someone might want to use it is for compatibility with MSVC++, where 
struct and class are improperly mangled differently.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-04 23:37                 ` Jason Merrill
@ 2019-12-05 23:33                   ` Martin Sebor
  2019-12-05 23:47                     ` Jakub Jelinek
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-12-05 23:33 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Jonathan Wakely

[-- Attachment #1: Type: text/plain, Size: 6592 bytes --]

On 12/4/19 4:37 PM, Jason Merrill wrote:
> On 12/3/19 4:49 PM, Martin Sebor wrote:
>> On 8/5/19 4:30 PM, Jason Merrill wrote:
>>> On Mon, Aug 5, 2019 at 5:50 PM Martin Sebor <msebor@gmail.com> wrote:
>>>>
>>>> On 8/5/19 1:25 PM, Jason Merrill wrote:
>>>>> On 8/1/19 7:35 PM, Martin Sebor wrote:
>>>>>> On 8/1/19 12:09 PM, Jason Merrill wrote:
>>>>>>> On 7/22/19 12:34 PM, Martin Sebor wrote:
>>>>>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-07/msg00622.html
>> ...
>>>>>>> -Wmismatched-tags is useful to have, given the MSVC/clang situation,
>>>>>>> but I wonder about memory consumption from all the record keeping.
>>>>>>> Do you have any overhead measurements?
>>>>>>
>>>>>> I did think about the overhead but not knowing if the patch would
>>>>>> even be considered I didn't spend time optimizing it.
>>>>>>
>>>>>> In an instrumented build of GCC with the patch I just did I have
>>>>>> collected the following stats for the number of elements in
>>>>>> the rec2loc hash table (for 998 files):
>>>>>>
>>>>>>     rec2loc elements   locvec elements
>>>>>>     min:           0                 0
>>>>>>     max:       2,785             3,303
>>>>>>     mean:        537             1,043
>>>>>>     median:      526             1,080
>>>>>>
>>>>>> The locvec data are based on totals for the whole hash table, so
>>>>>> in the worst case the hash_map has 2,785 elements, and the sum of
>>>>>> all locvec lengths in all those elements is 3,303.  Each rec2loc
>>>>>> element takes up 16 bytes, plus the size of the locvec data.
>>>>>>
>>>>>> If my math is right (which doesn't happen too often) in the worst
>>>>>> case the bookkeeping overhead is 43KB for the hash_map plus on
>>>>>> the order of 140KB for the vectors (just doubling the number of
>>>>>> elements to account for capacity = 2 X size, times 24 for
>>>>>> the flag_func_loc_t element type).  So 183K in the worst case
>>>>>> in GCC.
>>>>>
>>>>> 183k cumulative over all the GCC sources doesn't sound excessive, 
>>>>> but...
>>>>>
>>>>>> There are a few ways to reduce that if it seems excessive.
>>>>>>
>>>>>> One is by avoiding some waste in flag_func_loc_t which is
>>>>>>
>>>>>>     pair<tag_types, pair<bool, pair<tree, location_t>>>
>>>>>>
>>>>>> tree could come first and tag_types and the bool could share
>>>>>> space.  That should bring it down to 16 in LP64, for about
>>>>>> 30% off the 183K.
>>>>>>
>>>>>> Beyond that, we could change the algorithm to discard records
>>>>>> for prior declarations after the first definition has been seen
>>>>>> (and any mismatches diagnosed).
>>>>>>
>>>>>> We could also only collect one record for each definition
>>>>>> in system headers rather than one for every declaration and
>>>>>> reference.
>>>>>
>>>>> ...these all sound worthwhile.
>>>>
>>>> Okay, I'll look into it.
>>>>
>>>> To be clear: it was 183K maximum for a single compilation, not
>>>> aggregate for all of them.
>>>
>>> Aha.  Thanks.
>>
>> Attached is a revised patch that implements just -Wmismatched-tags
>> without the other two warnings (-Wclass-not-pod and -Wstruct-is-pod).
>> It also implements the optimizations mentioned above.  To make it
>> easier to do the tag cleanup by simply dropping the class-key when
>> it isn't necessary (suggested during the review of the cleanup patch)
>> I added another warning: -Wredundant-tags to point out instances
>> where the class-key or enum-key can safely be dropped. Both warnings
>> are off by default.
>>
>> With the optimizations in place, the biggest space overhead of
>> using the option I measured in a GCC build was 990 elements of
>> the record_to_locs_t hash table, plus 2756 elements of the locvec
>> vector.  In LP64, each record_to_locs_t element type is 16 bytes
>> and each element of the locvec vector is 24 bytes, so the maximum
>> space overhead is on the order of 80K.  The average overhead per
>> GCC translation unit was about 30K.
>>
>> The patch depends on fixes for a few bugs in GCC hash_tables (PR
>> 92761 and 92762).  I will post those separately.
>>
>> Martin
>>
>> PS Independently of this patch I will propose updating the GCC
>> Coding Conventions to remove the guideline to use the struct
>> class-key for PODs and class for non-PODs:
>>    https://gcc.gnu.org/codingconventions.html#Class_Use
> 
>> +class rec_decl_loc_t
> 
> Let's use "class" instead of "record" generally in this patch.

Okay.

> 
>> +/* A mapping between a TYPE_DECL for a class and the rec_decl_loc_t
>> +   description above.  */
>> +typedef hash_map<tree, rec_decl_loc_t> record_to_locs_t;
> 
> You might want to use hash_map<tree_decl_hash, rec_decl_loc_t> so you 
> get the decl-specific hash function rather than the generic pointer hash.

Done.

> 
> It's hard to distinguish between this type and the previous one by name; 
> this one should probably have "map" in its name.
> 
>> +static GTY (()) record_to_locs_t *rec2loc;
> ...
>> +    rec2loc = new record_to_locs_t ();
> 
> If this isn't GC-allocated, marking it with GTY(()) seems wrong.  How do 
> you imagine this warning interacting with PCH?

I have to confess I know too little about PCH to have an idea how
it might interact.  Is there something you suggest I try testing?

> 
>> +  /* A prior declaration of TYPE_DECL has been seen.  */
> 
> The rest of this function from this point on seems like it would be 
> better as a member function of rec_decl_loc_t.
> 
>> +static void
>> +diag_mismatched_tags (tree type_decl, rec_decl_loc_t &recloc)
> 
> This could also be a member function.

I like that idea.  I've made more use of member functions in
the revised patch.  That let me make the implementation details
of the new class private.  (I'd like it even better if I could
move it into a file of its own, and avoid growing the already
sizable parser.c even more.)

> 
>> +  if (rdl->idxdef < UINT_MAX && rdl->def_class_key == class_key)
> 
> Maybe != rather than < ?
> 
>> +@item -Wmismatched-tags @r{(C++ and Objective-C++ only)}
> 
> The documentation of this flag should mention that the main reason 
> someone might want to use it is for compatibility with MSVC++, where 
> struct and class are improperly mangled differently.

I've added a sentence about the mangling.

Attached is the revised patch.

Martin

[-- Attachment #2: gcc-61339.diff --]
[-- Type: text/x-patch, Size: 37216 bytes --]

PR c++/61339 - add warning for mismatch between struct and class

gcc/c-family/ChangeLog:

	PR c++/61339
	* c.opt (-Wmismatched-tags, -Wredundant-tags): New options.

gcc/cp/ChangeLog:

	PR c++/61339
	* parser.c (cp_parser_maybe_warn_enum_key): New function.
	(class_decl_loc_t): New class.
	(class_to_loc_map_t): New typedef.
	(class2loc): New global variable.
	(cp_parser_elaborated_type_specifier): Call
	cp_parser_maybe_warn_enum_key.
	(cp_parser_class_head): Call cp_parser_check_class_key.
	(cp_parser_check_class_key): Add arguments.  Call class_decl_loc_t::add.
	(c_parse_file): Call class_decl_loc_t::diag_mismatched_tags.

gcc/testsuite/ChangeLog:

	PR c++/61339
	* g++.dg/warn/Wmismatched-tags.C: New test.
	* g++.dg/warn/Wredundant-tags.C: New test.

gcc/ChangeLog:

	PR c++/61339
	* doc/invoke.texi (-Wmismatched-tags, -Wredundant-tags): Document
	new C++ options.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 914a2f0ef44..4f3d3cf0d43 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -755,6 +755,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 or referenced 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.
@@ -783,6 +787,10 @@ Wpacked-not-aligned
 C ObjC C++ ObjC++ Var(warn_packed_not_aligned) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn when fields in a struct with the packed attribute are misaligned.
 
+Wredundant-tags
+C++ Objc++ Var(warn_redundant_tags) Warning
+Warn when a class or enumerated type is referenced using a redundant class-key.
+
 Wsized-deallocation
 C++ ObjC++ Var(warn_sized_deallocation) Warning EnabledBy(Wextra)
 Warn about missing sized deallocation functions.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index fb030022627..555466d2591 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2599,8 +2599,9 @@ static enum tag_types cp_parser_token_is_class_key
   (cp_token *);
 static enum tag_types cp_parser_token_is_type_parameter_key
   (cp_token *);
+static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid);
 static void cp_parser_check_class_key
-  (enum tag_types, tree type);
+(cp_parser *, location_t, enum tag_types, tree type, bool, bool);
 static void cp_parser_check_access_in_redeclaration
   (tree type, location_t location);
 static bool cp_parser_optional_template_keyword
@@ -18480,6 +18481,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   tree globalscope;
   cp_token *token = NULL;
 
+  /* For class and enum types the location of the class-key or enum-key.  */
+  location_t key_loc = cp_lexer_peek_token (parser->lexer)->location;
+  /* For a scoped enum, the 'class' or 'struct' keyword id.  */
+  rid scoped_key = RID_MAX;
+
   /* See if we're looking at the `enum' keyword.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM))
     {
@@ -18490,10 +18496,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
       /* Issue a warning if the `struct' or `class' key (for C++0x scoped
 	 enums) is used here.  */
       cp_token *token = cp_lexer_peek_token (parser->lexer);
-      if (cp_parser_is_keyword (token, RID_CLASS)
-	  || cp_parser_is_keyword (token, RID_STRUCT))
+      if (cp_parser_is_keyword (token, scoped_key = RID_CLASS)
+	  || cp_parser_is_keyword (token, scoped_key = RID_STRUCT))
 	{
-	  gcc_rich_location richloc (token->location);
+	  location_t loc = token->location;
+	  gcc_rich_location richloc (loc);
 	  richloc.add_range (input_location);
 	  richloc.add_fixit_remove ();
 	  pedwarn (&richloc, 0, "elaborated-type-specifier for "
@@ -18501,7 +18508,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		   token->u.value);
 	  /* Consume the `struct' or `class' and parse it anyway.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  /* Create a combined location for the whole scoped-enum-key.  */
+	  key_loc = make_location (key_loc, key_loc, loc);
 	}
+      else
+	scoped_key = RID_MAX;
+
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
     }
@@ -18517,6 +18529,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   /* Otherwise it must be a class-key.  */
   else
     {
+      key_loc = cp_lexer_peek_token (parser->lexer)->location;
       tag_type = cp_parser_class_key (parser);
       if (tag_type == none_type)
 	return error_mark_node;
@@ -18827,13 +18840,18 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		 "attributes ignored on elaborated-type-specifier that is not a forward declaration");
     }
 
-  if (tag_type != enum_type)
+  if (tag_type == enum_type)
+    cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
+  else
     {
+      /* Diagnose class/struct/union mismatches.  */
+      cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
+				 cp_parser_declares_only_class_p (parser));
+
       /* 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
@@ -24371,11 +24389,14 @@ cp_parser_class_head (cp_parser* parser,
 		       parser->num_template_parameter_lists);
     }
 
+  /* Diagnose class/struct/union mismatches.  */
+  cp_parser_check_class_key (parser, UNKNOWN_LOCATION, class_key, type,
+			     true, 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:
@@ -30598,14 +30619,162 @@ cp_parser_token_is_type_parameter_key (cp_token* token)
     }
 }
 
-/* Issue an error message if the CLASS_KEY does not match the TYPE.  */
+/* Diagnose redundant enum-keys.  */
+
+static void
+cp_parser_maybe_warn_enum_key (cp_parser *parser, location_t key_loc,
+			       tree type, rid scoped_key)
+{
+  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);
+
+  /* The enum-key is redundant for uses of the TYPE that are not
+     declarations and for which name lookup returns just the type
+     itself.  */
+  if (decl == type_decl)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant enum-key %<enum%s%> in reference to %q#T",
+		  (scoped_key == RID_CLASS ? " class"
+		   : scoped_key == RID_STRUCT ? " struct" : ""), type);
+    }
+}
+
+/* Describes the set of declarations of a struct, class, or class template
+   or its specializations.  Used for -Wmismatched-tags.  */
+
+class class_decl_loc_t
+{
+ public:
+
+  class_decl_loc_t ()
+    : locvec (), idxdef (), def_class_key ()
+  {
+    locvec.create (4);
+  }
+
+  /* Constructs 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 an ambiguity with
+     another symbol with the same name.
+     DEF_P is true for a class declaration that is a definition.  */
+  class_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);
+    class_key_loc_t ckl (current_function_decl, input_location,
+			 class_key, key_redundant);
+    locvec.quick_push (ckl);
+  }
+
+  /* Copy, assign, and destroy the object.  Necessary because LOCVEC
+     isn't safely copyable and assignable and doesn't release storage
+     on its own.  */
+  class_decl_loc_t (const class_decl_loc_t &rhs)
+    : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef),
+      def_class_key (rhs.def_class_key)
+  { }
+
+  class_decl_loc_t& operator= (const class_decl_loc_t &rhs)
+  {
+    if (this == &rhs)
+      return *this;
+    locvec.release ();
+    locvec = rhs.locvec.copy ();
+    idxdef = rhs.idxdef;
+    def_class_key = rhs.def_class_key;
+    return *this;
+  }
+
+  ~class_decl_loc_t ()
+  {
+    locvec.release ();
+  }
+
+/* Issues -Wmismatched-tags for a single class.  */
+  void diag_mismatched_tags (tree);
+
+/* Issues -Wmismatched-tags for all classes.  */
+  static void diag_mismatched_tags ();
+
+/* Adds TYPE_DECL to the collection of class decls.  */
+  static void add (tree, tag_types, bool, bool);
+
+private:
+
+  tree function (unsigned i) const
+  {
+    return locvec[i].func;
+  }
+
+  location_t location (unsigned i) const
+  {
+    return locvec[i].loc;
+  }
+
+  bool key_redundant (unsigned i) const
+  {
+    return locvec[i].key_redundant;
+  }
+
+  tag_types class_key (unsigned i) const
+  {
+    return locvec[i].class_key;
+  }
+
+  /* The location of a single mention of a class type with the given
+     class-key.  */
+  struct class_key_loc_t
+  {
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+
+    /* The function the type is mentioned in.  */
+    tree func;
+    /* The exact location.  */
+    location_t loc;
+    /* The class-key used in the mention of the type.  */
+    tag_types class_key;
+    /* True when the class-key could be omitted at this location
+       without an ambiguity with another symbol of the same name.  */
+    bool key_redundant;
+  };
+  /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
+  vec <class_key_loc_t> locvec;
+  /* LOCVEC index of the definition or UINT_MAX 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 for a class and the class_decl_loc_t
+   description above.  */
+typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t;
+static class_to_loc_map_t class2loc;
+
+/* Issue an error message if the CLASS_KEY does not match the TYPE.
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a declaration of class TYPE and clear for a reference to
+   it that is not a declaration of it.  */
 
 static void
-cp_parser_check_class_key (enum tag_types class_key, tree type)
+cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
+			   tag_types class_key, tree type, bool def_p,
+			   bool decl_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"
@@ -30613,7 +30782,213 @@ 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 (!warn_mismatched_tags && !warn_redundant_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);
+
+  /* The class-key is redundant for uses of the CLASS_TYPE that are
+     neither definitions of it nor declarations, and for which name
+     lookup returns just the type itself.  */
+  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		"redundant class-key %qs in reference to %q#T",
+		class_key == union_type ? "union"
+		: class_key == record_type ? "struct" : "class",
+		type);
     }
+
+  if (seen_as_union || !warn_mismatched_tags)
+    return;
+
+  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
+}
+
+/* Adds TYPE_DECL to the collection of class decls.  */
+
+void
+class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
+		       bool def_p)
+{
+  class_decl_loc_t *rdl = class2loc.get (type_decl);
+  if (!rdl)
+    {
+      /* TYPE_DECL is the first declaration of the type.  Just create
+	 a new entry for it.  */
+      class2loc.put (type_decl, class_decl_loc_t (class_key, false, def_p));
+      return;
+    }
+
+  /* A prior declaration of TYPE_DECL has been seen.  */
+
+  if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
+    /* Do nothing if the class-key in this declaration matches
+       the definition.  */
+    return;
+
+  /* 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 ();
+
+  /* Append a record of this declaration to the vector.  */
+  class_decl_loc_t::class_key_loc_t
+    ckl (current_function_decl, input_location, class_key, redundant);
+  rdl->locvec.safe_push (ckl);
+
+  if (rdl->idxdef < UINT_MAX)
+    {
+      /* As a space optimization diagnose declarations of a class
+	 whose definition has been seen and purge the LOCVEC of
+	 all entries except the definition.  */
+      rdl->diag_mismatched_tags (type_decl);
+      if (rdl->idxdef)
+	{
+	  class_decl_loc_t::class_key_loc_t ent = rdl->locvec[rdl->idxdef];
+	  rdl->locvec.release ();
+	  rdl->locvec.reserve (2);
+	  rdl->locvec.safe_push (ent);
+	  rdl->idxdef = 0;
+	}
+      else
+	/* Pop the entry pushed above for this declaration.  */
+	rdl->locvec.pop ();
+    }
+}
+
+/* Issues -Wmismatched-tags for a single class.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags (tree type_decl)
+{
+  unsigned ndecls = locvec.length ();
+
+  /* Skip a declaration that consistently uses the same class-key
+     or one with just a solitary declaration (i.e., TYPE_DECL). */
+  if (def_class_key != none_type || ndecls < 2)
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
+  /* Set if a class definition for RECLOC has been seen.  */
+  bool def_p = idxdef < ndecls;
+  unsigned idxguide = def_p ? 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 (class_key (idx) == class_key (idxguide))
+    if (++idx == ndecls)
+      return;
+
+  /* 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 xpect_key = class_key (def_p ? idxguide : 0);
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+  /* Set the function declaration to print in diagnostic context.  */
+  current_function_decl = function (idx);
+
+  location_t loc = location (idx);
+  bool key_redundant_p = key_redundant (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 (loc, OPT_Wmismatched_tags,
+	      "%qT declared with a mismatched class-key %qs",
+	      type_decl, xmatchkstr);
+
+  /* Suggest how to avoid the warning for each instance since
+     the guidance may be different depending on context.  */
+  inform (loc,
+	  (key_redundant_p
+	   ? G_("remove the class-key or replace it with %qs")
+	   : G_("replace the class-key with %qs")),
+	  xpectkstr);
+
+  /* Mention the first declaration or definition that guided
+     the decision to issue the warning above.  */
+  tag_types clskey = class_key (idxguide);
+  inform (location (idxguide),
+	  (def_p
+	   ? G_("%qT defined as %qs here")
+	   : G_("%qT first declared as %qs here")),
+	  type_decl, xpectkstr);
+
+  /* Issue warnings for the remaining inconsistent declarations.  */
+  for (unsigned i = idx + 1; i != ndecls; ++i)
+    {
+      clskey = class_key (i);
+      /* Skip over the declarations that match either the definition
+	 if one was provided or the first declaration.  */
+      if (clskey == xpect_key)
+	continue;
+
+      loc = location (i);
+      key_redundant_p = key_redundant (i);
+      /* Set the function declaration to print in diagnostic context.  */
+      current_function_decl = function (i);
+      warning_at (loc, OPT_Wmismatched_tags,
+		  "%qT declared with a mismatched class-key %qs",
+		  type_decl, xmatchkstr);
+      /* Suggest how to avoid the warning for each instance since
+	 the guidance may be different depending on context.  */
+      inform (loc,
+	      (key_redundant_p
+	       ? G_("remove the class-key or replace it with %qs")
+	       : G_("replace the class-key with %qs")),
+	      xpectkstr);
+    }
+
+  /* Restore the current function in case it was replaced above.  */
+  current_function_decl = save_func;
+}
+
+/* Issues -Wmismatched-tags for all classes.  Called at the end
+   of processing a translation unit, after declarations of all class
+   types and their uses have been recorded.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags ()
+{
+  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
+  gcc_assert (warn_mismatched_tags || class2loc.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 class_to_loc_map_t::iterator iter_t;
+  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+    {
+      tree type_decl = (*it).first;
+      class_decl_loc_t &recloc = (*it).second;
+      recloc.diag_mismatched_tags (type_decl);
+    }
+
+  class2loc.empty ();
+  /* Restore the current function.  */
+  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
@@ -43046,6 +43421,8 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+  class_decl_loc_t::diag_mismatched_tags ();
+
   the_parser = NULL;
 
   finish_translation_unit ();
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d165f31a865..817c114321a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -236,7 +236,7 @@ in the following sections.
 -Wliteral-suffix @gol
 -Wmultiple-inheritance  -Wno-init-list-lifetime @gol
 -Wnamespaces  -Wnarrowing @gol
--Wpessimizing-move  -Wredundant-move @gol
+-Wpessimizing-move  -Wredundant-move -Wredundant-tags @gol
 -Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
 -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
@@ -3323,6 +3323,21 @@ treats the return value as if it were designated by an rvalue.
 
 This warning is enabled by @option{-Wextra}.
 
+@item -Wredundant-tags @r{(C++ and Objective-C++ only)}
+@opindex Wredundant-tags
+@opindex Wno-redundant-tags
+Warn about redundant class-key and enum-key in references to class types
+and enumerated types in contexts where the key can be eliminated without
+causing an ambiguity.  For example
+
+@smallexample
+struct foo;
+struct foo *p;   // -Wredundant-tags, keyword struct can be eliminated
+
+void foo ();   // "hides" struct foo
+void bar (struct foo&);   // no warning, keyword struct cannot be eliminated
+@end smallexample
+
 @item -fext-numeric-literals @r{(C++ and Objective-C++ only)}
 @opindex fext-numeric-literals
 @opindex fno-ext-numeric-literals
@@ -3458,6 +3473,32 @@ 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.  In code that is
+intended to be portable to Windows-based compilers the warning helps prevent
+unresolved references due to the difference in the mangling of symbols
+declared with different class-keys.  The option can be used either on its
+own or in conjunction with @option{-Wredundant-tags}.
+
 @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)}
 @opindex Wmultiple-inheritance
 @opindex Wno-multiple-inheritance
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..36a7903234a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
@@ -0,0 +1,278 @@
+/* PR c++/61339 - add mismatch between struct and class
+   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" } */
+
+namespace Classes
+{
+class A;
+class A;
+
+struct B;
+struct B;
+
+union C;
+union C;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key 'struct'" }
+class D { };                // { dg-message "Classes::D' defined as 'class' here" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key 'class'" }
+struct E { };               // { dg-message "Classes::E' defined as 'struct' here" }
+
+class D;
+struct E;
+
+class D;
+struct E;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key" }
+
+class F;                    // { dg-message "Classes::F' first declared as 'class' here" }
+class F;
+
+struct G { };               // { dg-message "Classes::G' defined as 'struct' here" }
+}   // namespace Classes
+
+
+namespace Classes
+{
+class A;
+struct B;
+union C;
+class D;
+struct E;
+
+struct F;                   // { dg-warning "Classes::F' declared with a mismatched class-key" }
+
+struct G;
+}
+
+// Verify that the correct hint is provided, one to remove the class-key
+// when it's redundant, and one to (only) replace it with the correct one
+// when it's needed to disambiguate the reference to the class type.
+namespace RemoveOrReplace
+{
+struct Func;
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to remove" { target *-*-* } .-1 }
+
+void Func ();
+
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to replace" { target *-*-* } .-1 }
+
+class Var;
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to remove" { target *-*-* } .-1 }
+void f (struct Var*);       // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+int Var;
+
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to replace" { target *-*-* } .-1 }
+}
+
+namespace GlobalObjects
+{
+class A;                    // { dg-message "'GlobalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'GlobalObjects::B' first declared as 'struct' here" }
+class C { };                // { dg-message "'GlobalObjects::C' defined as 'class' here" }
+
+extern A a0;
+extern class A a1;
+extern class A a2;
+
+extern B b0;
+extern struct B b1;
+extern struct B b2;
+
+extern struct A a3;         // { dg-warning "GlobalObjects::A' declared with a mismatched class-key" }
+extern class A a4;
+
+extern class B b3;          // { dg-warning "GlobalObjects::B' declared with a mismatched class-key" }
+extern struct B b4;
+
+extern struct C c[];        // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+extern char
+arr[sizeof (struct C)];     // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+}   // namespace GlobalObjects
+
+
+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 (A*, B&)
+{
+  class A *a1;
+  class A *a2;
+
+  struct B *b1;
+  struct B *b2;
+
+  struct A *a3;             // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+  class A *a4;
+
+  class B *b3;              // { dg-warning "LocalObjects::B' declared with a mismatched class-key" }
+  struct B *b4;
+}
+
+void g (struct A*);         // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+
+}   // namespace LocalObjects
+
+
+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 "MemberClasses::G::H' declared with a mismatched class-key" }
+  class I { };              // { dg-message "MemberClasses::G::I' defined as 'class' here" }
+  struct I;                 // { dg-warning "MemberClasses::G::I' declared with a mismatched class-key" }
+};
+}   // namespace MemberClasses
+
+
+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 "DataMembers::G' declared with a mismatched class-key" }
+  class G *p1;
+
+  struct H &r0;
+  class H &r1;              // { dg-warning "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 "DataMembers::I::K' declared with a mismatched class-key" }
+
+  struct J j1;              // { dg-warning "DataMembers::I::J' declared with a mismatched class-key" }
+  struct K k1;
+};
+}   // namespace DataMembers
+
+
+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 "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+class D                     // { dg-message "Templates::D\[^\n\r]*' defined as 'class' here" }
+{ public: D (); };
+
+template <int> class E;     // { dg-warning "Templates::E\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+struct E                    // { dg-message "Templates::E\[^\n\r]*' defined as 'struct' here" }
+{ int i; };
+
+template <int> class D;
+template <int> struct E;
+
+template <int>
+struct D;                   // { dg-warning "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+                            // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 }
+}   // namespace Templates
+
+
+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 "ExplicitSpecializations::D\[^\n\r]*' declared with a mismatched class-key " }
+template <>
+struct D<0> { };            // { dg-message "ExplicitSpecializations::D\[^\n\r]*' defined as 'struct' here" }
+
+template <int> struct E;
+template <> struct E<0>;    // { dg-warning "ExplicitSpecializations::E\[^\n\r]*' declared with a mismatched class-key" }
+template <>
+class E<0> { };             // { dg-message "ExplicitSpecializations::E\[^\n\r]*' defined as 'class' here" }
+
+template <int> struct F;
+template <> class F<0> { }; // { dg-message "ExplicitSpecializations::F\[^\n\r]*' defined as 'class' here" }
+
+template <>
+struct F<0>;                // { dg-warning "ExplicitSpecializations::F\[^\n\r]*' declared with a mismatched class-key" }
+}   // namespace ExplicitSpecializations
+
+
+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 "PartialSpecializations::E<const T>' first declared as 'struct' here" }
+
+template <class T>
+class E<const T>;           // { dg-warning "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 "PartialSpecializations::F<const T>' declared with a mismatched class-key" }
+}   // namespace PartialSpecializations
+
+
+namespace Classes
+{
+struct G;
+
+class G;                    // { dg-warning "Classes::G' declared with a mismatched class-key 'class'" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
new file mode 100644
index 00000000000..ac5afa912a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
@@ -0,0 +1,128 @@
+/* PR c++/61339 - add mismatch between struct and class
+   Test to verify that -Wredundant-tags is issued for references to class
+   types that use the class-key even though they don't need to.
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+struct A;
+
+extern A *pa;
+extern struct A *pa;        // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+extern A aa[];
+extern struct A aa[];       // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+void func (A*);
+void func (struct A*);      // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+int A;
+
+extern struct A *pa;
+extern struct A aa[];
+void func (struct A*);
+
+
+class B;
+
+extern B *pb;
+extern class B *pb;         // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+extern B ab[];
+extern class B ab[];        // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+void func (B*);
+void func (class B*);       // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+int B;
+
+extern class B *pb;
+extern class B ab[];
+void func (class B*);
+
+
+enum C { c0 };
+
+extern C *pc;
+extern enum C *pc;          // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+extern C ac[];
+extern enum C ac[];         // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+void func (C*);
+void func (enum C*);        // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+int C;
+
+extern enum C *pc;
+extern enum C ac[];
+void func (enum C*);
+
+
+#if __cplusplus > 199711L
+
+enum class D1 { d1 };
+enum struct D2 { d2 };
+
+#else
+
+enum D1 { d1 };
+enum D2 { d2 };
+
+#endif
+
+extern D1 *pd1;
+extern D2 *pd2;
+extern enum D1 *pd1;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } .-1 }
+
+extern enum D2 *pd2;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D2'" "C++ 98" { target c++98_only } .-1 }
+
+extern D1 ad1[];
+extern D2 ad2[];
+
+#if __cplusplus > 199711L
+extern enum class D1 ad1[]; // { dg-warning "redundant enum-key 'enum class' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'class' keyword" "C++ 11 and above" { target c++11 } .-1 }
+/* The pretty printer cannot differentiate between enum class and enum struct
+   because the C++ front-end doesn't encode it so allow for both in the text
+   of the warning below.  */
+extern enum struct D2 ad2[]; // { dg-warning "redundant enum-key 'enum struct' in reference to 'enum \(class|struct\) D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'struct' keyword" "C++ 11 and above" { target c++11 } .-1 }
+#else
+extern enum D1 ad1[];       // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } }
+#endif
+
+void func (D1*);
+void func (enum D1*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+void func (D2*);
+void func (enum D2*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+int D1, D2;
+
+extern enum D1 *pd1;
+extern enum D1 ad1[];
+void func (enum D1*);
+
+extern enum D2 *pd2;
+extern enum D2 ad2[];
+void func (enum D2*);
+
+
+union U;
+
+extern U *pu;
+extern union U *pu;         // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+extern U au[];
+extern union U au[];        // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+void func (U*);
+void func (union U*);       // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+int U;
+
+extern union U *pu;
+extern union U au[];
+void func (union U*);

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-05 23:33                   ` Martin Sebor
@ 2019-12-05 23:47                     ` Jakub Jelinek
  2019-12-06 19:08                       ` Jason Merrill
  0 siblings, 1 reply; 47+ messages in thread
From: Jakub Jelinek @ 2019-12-05 23:47 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, gcc-patches, Jonathan Wakely

On Thu, Dec 05, 2019 at 04:33:10PM -0700, Martin Sebor wrote:
> > It's hard to distinguish between this type and the previous one by name;
> > this one should probably have "map" in its name.
> > 
> > > +static GTY (()) record_to_locs_t *rec2loc;
> > ...
> > > +    rec2loc = new record_to_locs_t ();
> > 
> > If this isn't GC-allocated, marking it with GTY(()) seems wrong.  How do
> > you imagine this warning interacting with PCH?
> 
> I have to confess I know too little about PCH to have an idea how
> it might interact.  Is there something you suggest I try testing?

For your patch, obviously some struct/class forward declarations or
definitions in a header that you compile into PCH and then the main testcase
that contains the mismatched pairs.

If there is something that you need to record during parsing of the
precompiled header and use later on, everything needs to be GGC allocated.
So, the hash_map needs to be created with something like
hash_map<something, something_else>::create_ggc (nnn)
and it really can't use pointer hashing, but has to use some different one
(say on DECL_UID, TYPE_UID etc.), because the addresses are remapped during
PCH save/restore cycle, but hash tables aren't rehashed.
See e.g. PR92458.

	Jakub

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-05 23:47                     ` Jakub Jelinek
@ 2019-12-06 19:08                       ` Jason Merrill
  2019-12-10  0:29                         ` Martin Sebor
  0 siblings, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-12-06 19:08 UTC (permalink / raw)
  To: Jakub Jelinek, Martin Sebor; +Cc: gcc-patches, Jonathan Wakely

On 12/5/19 6:47 PM, Jakub Jelinek wrote:
> On Thu, Dec 05, 2019 at 04:33:10PM -0700, Martin Sebor wrote:
>>> It's hard to distinguish between this type and the previous one by name;
>>> this one should probably have "map" in its name.
>>>
>>>> +static GTY (()) record_to_locs_t *rec2loc;
>>> ...
>>>> +    rec2loc = new record_to_locs_t ();
>>>
>>> If this isn't GC-allocated, marking it with GTY(()) seems wrong.  How do
>>> you imagine this warning interacting with PCH?
>>
>> I have to confess I know too little about PCH to have an idea how
>> it might interact.  Is there something you suggest I try testing?
> 
> For your patch, obviously some struct/class forward declarations or
> definitions in a header that you compile into PCH and then the main testcase
> that contains the mismatched pairs.
> 
> If there is something that you need to record during parsing of the
> precompiled header and use later on, everything needs to be GGC allocated.
> So, the hash_map needs to be created with something like
> hash_map<something, something_else>::create_ggc (nnn)
> and it really can't use pointer hashing, but has to use some different one
> (say on DECL_UID, TYPE_UID etc.), because the addresses are remapped during
> PCH save/restore cycle, but hash tables aren't rehashed.
> See e.g. PR92458.

Alternately you can decide that this information will not be saved to 
PCH, and rely on CLASSTYPE_DECLARED_CLASS for classes loaded from a PCH.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-06 19:08                       ` Jason Merrill
@ 2019-12-10  0:29                         ` Martin Sebor
  2019-12-16 16:35                           ` Martin Sebor
  2019-12-16 23:01                           ` Jason Merrill
  0 siblings, 2 replies; 47+ messages in thread
From: Martin Sebor @ 2019-12-10  0:29 UTC (permalink / raw)
  To: Jason Merrill, Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely

[-- Attachment #1: Type: text/plain, Size: 1868 bytes --]

On 12/6/19 12:08 PM, Jason Merrill wrote:
> On 12/5/19 6:47 PM, Jakub Jelinek wrote:
>> On Thu, Dec 05, 2019 at 04:33:10PM -0700, Martin Sebor wrote:
>>>> It's hard to distinguish between this type and the previous one by 
>>>> name;
>>>> this one should probably have "map" in its name.
>>>>
>>>>> +static GTY (()) record_to_locs_t *rec2loc;
>>>> ...
>>>>> +    rec2loc = new record_to_locs_t ();
>>>>
>>>> If this isn't GC-allocated, marking it with GTY(()) seems wrong.  
>>>> How do
>>>> you imagine this warning interacting with PCH?
>>>
>>> I have to confess I know too little about PCH to have an idea how
>>> it might interact.  Is there something you suggest I try testing?
>>
>> For your patch, obviously some struct/class forward declarations or
>> definitions in a header that you compile into PCH and then the main 
>> testcase
>> that contains the mismatched pairs.
>>
>> If there is something that you need to record during parsing of the
>> precompiled header and use later on, everything needs to be GGC 
>> allocated.
>> So, the hash_map needs to be created with something like
>> hash_map<something, something_else>::create_ggc (nnn)
>> and it really can't use pointer hashing, but has to use some different 
>> one
>> (say on DECL_UID, TYPE_UID etc.), because the addresses are remapped 
>> during
>> PCH save/restore cycle, but hash tables aren't rehashed.
>> See e.g. PR92458.
> 
> Alternately you can decide that this information will not be saved to 
> PCH, and rely on CLASSTYPE_DECLARED_CLASS for classes loaded from a PCH.

This seems like the right approach to me.  Mismatches in
a precompiled header should be diagnosed when the header is
being compiled, so the only ones involving its uses should
be between classes defined in it and declared or referenced
outside it.  I've implemented this in the attached revision.

Martin

[-- Attachment #2: gcc-61339.diff --]
[-- Type: text/x-patch, Size: 39207 bytes --]

PR c++/61339 - add warning for mismatch between struct and class

gcc/c-family/ChangeLog:

	PR c++/61339
	* c.opt (-Wmismatched-tags, -Wredundant-tags): New options.

gcc/cp/ChangeLog:

	PR c++/61339
	* parser.c (cp_parser_maybe_warn_enum_key): New function.
	(class_decl_loc_t): New class.
	(class_to_loc_map_t): New typedef.
	(class2loc): New global variable.
	(cp_parser_elaborated_type_specifier): Call
	cp_parser_maybe_warn_enum_key.
	(cp_parser_class_head): Call cp_parser_check_class_key.
	(cp_parser_check_class_key): Add arguments.  Call class_decl_loc_t::add.
	(c_parse_file): Call class_decl_loc_t::diag_mismatched_tags.

gcc/testsuite/ChangeLog:

	PR c++/61339
	* g++.dg/warn/Wmismatched-tags.C: New test.
	* g++.dg/warn/Wredundant-tags.C: New test.
	* g++.dg/pch/Wmismatched-tags.C: New test.
	* g++.dg/pch/Wmismatched-tags.Hs: New test header.

gcc/ChangeLog:

	PR c++/61339
	* doc/invoke.texi (-Wmismatched-tags, -Wredundant-tags): Document
	new C++ options.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 914a2f0ef44..4f3d3cf0d43 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -755,6 +755,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 or referenced 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.
@@ -783,6 +787,10 @@ Wpacked-not-aligned
 C ObjC C++ ObjC++ Var(warn_packed_not_aligned) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn when fields in a struct with the packed attribute are misaligned.
 
+Wredundant-tags
+C++ Objc++ Var(warn_redundant_tags) Warning
+Warn when a class or enumerated type is referenced using a redundant class-key.
+
 Wsized-deallocation
 C++ ObjC++ Var(warn_sized_deallocation) Warning EnabledBy(Wextra)
 Warn about missing sized deallocation functions.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index fb030022627..7915c7416aa 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2599,8 +2599,9 @@ static enum tag_types cp_parser_token_is_class_key
   (cp_token *);
 static enum tag_types cp_parser_token_is_type_parameter_key
   (cp_token *);
+static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid);
 static void cp_parser_check_class_key
-  (enum tag_types, tree type);
+(cp_parser *, location_t, enum tag_types, tree type, bool, bool);
 static void cp_parser_check_access_in_redeclaration
   (tree type, location_t location);
 static bool cp_parser_optional_template_keyword
@@ -18480,6 +18481,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   tree globalscope;
   cp_token *token = NULL;
 
+  /* For class and enum types the location of the class-key or enum-key.  */
+  location_t key_loc = cp_lexer_peek_token (parser->lexer)->location;
+  /* For a scoped enum, the 'class' or 'struct' keyword id.  */
+  rid scoped_key = RID_MAX;
+
   /* See if we're looking at the `enum' keyword.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM))
     {
@@ -18490,10 +18496,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
       /* Issue a warning if the `struct' or `class' key (for C++0x scoped
 	 enums) is used here.  */
       cp_token *token = cp_lexer_peek_token (parser->lexer);
-      if (cp_parser_is_keyword (token, RID_CLASS)
-	  || cp_parser_is_keyword (token, RID_STRUCT))
+      if (cp_parser_is_keyword (token, scoped_key = RID_CLASS)
+	  || cp_parser_is_keyword (token, scoped_key = RID_STRUCT))
 	{
-	  gcc_rich_location richloc (token->location);
+	  location_t loc = token->location;
+	  gcc_rich_location richloc (loc);
 	  richloc.add_range (input_location);
 	  richloc.add_fixit_remove ();
 	  pedwarn (&richloc, 0, "elaborated-type-specifier for "
@@ -18501,7 +18508,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		   token->u.value);
 	  /* Consume the `struct' or `class' and parse it anyway.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  /* Create a combined location for the whole scoped-enum-key.  */
+	  key_loc = make_location (key_loc, key_loc, loc);
 	}
+      else
+	scoped_key = RID_MAX;
+
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
     }
@@ -18517,6 +18529,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   /* Otherwise it must be a class-key.  */
   else
     {
+      key_loc = cp_lexer_peek_token (parser->lexer)->location;
       tag_type = cp_parser_class_key (parser);
       if (tag_type == none_type)
 	return error_mark_node;
@@ -18827,13 +18840,18 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		 "attributes ignored on elaborated-type-specifier that is not a forward declaration");
     }
 
-  if (tag_type != enum_type)
+  if (tag_type == enum_type)
+    cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
+  else
     {
+      /* Diagnose class/struct/union mismatches.  */
+      cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
+				 cp_parser_declares_only_class_p (parser));
+
       /* 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
@@ -24371,11 +24389,14 @@ cp_parser_class_head (cp_parser* parser,
 		       parser->num_template_parameter_lists);
     }
 
+  /* Diagnose class/struct/union mismatches.  */
+  cp_parser_check_class_key (parser, UNKNOWN_LOCATION, class_key, type,
+			     true, 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:
@@ -30598,14 +30619,163 @@ cp_parser_token_is_type_parameter_key (cp_token* token)
     }
 }
 
-/* Issue an error message if the CLASS_KEY does not match the TYPE.  */
+/* Diagnose redundant enum-keys.  */
 
 static void
-cp_parser_check_class_key (enum tag_types class_key, tree type)
+cp_parser_maybe_warn_enum_key (cp_parser *parser, location_t key_loc,
+			       tree type, rid scoped_key)
+{
+  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);
+
+  /* The enum-key is redundant for uses of the TYPE that are not
+     declarations and for which name lookup returns just the type
+     itself.  */
+  if (decl == type_decl)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant enum-key %<enum%s%> in reference to %q#T",
+		  (scoped_key == RID_CLASS ? " class"
+		   : scoped_key == RID_STRUCT ? " struct" : ""), type);
+    }
+}
+
+/* Describes the set of declarations of a struct, class, or class template
+   or its specializations.  Used for -Wmismatched-tags.  */
+
+class class_decl_loc_t
+{
+ public:
+
+  class_decl_loc_t ()
+    : locvec (), idxdef (), def_class_key ()
+  {
+    locvec.create (4);
+  }
+
+  /* Constructs 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 an ambiguity with
+     another symbol with the same name.
+     DEF_P is true for a class declaration that is a definition.  */
+  class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
+		    location_t curloc = input_location)
+    : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
+  {
+    locvec.create (4);
+    class_key_loc_t ckl (current_function_decl, curloc, class_key,
+			 key_redundant);
+    locvec.quick_push (ckl);
+  }
+
+  /* Copy, assign, and destroy the object.  Necessary because LOCVEC
+     isn't safely copyable and assignable and doesn't release storage
+     on its own.  */
+  class_decl_loc_t (const class_decl_loc_t &rhs)
+    : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef),
+      def_class_key (rhs.def_class_key)
+  { }
+
+  class_decl_loc_t& operator= (const class_decl_loc_t &rhs)
+  {
+    if (this == &rhs)
+      return *this;
+    locvec.release ();
+    locvec = rhs.locvec.copy ();
+    idxdef = rhs.idxdef;
+    def_class_key = rhs.def_class_key;
+    return *this;
+  }
+
+  ~class_decl_loc_t ()
+  {
+    locvec.release ();
+  }
+
+/* Issues -Wmismatched-tags for a single class.  */
+  void diag_mismatched_tags (tree);
+
+/* Issues -Wmismatched-tags for all classes.  */
+  static void diag_mismatched_tags ();
+
+/* Adds TYPE_DECL to the collection of class decls.  */
+  static void add (tree, tag_types, bool, bool);
+
+private:
+
+  tree function (unsigned i) const
+  {
+    return locvec[i].func;
+  }
+
+  location_t location (unsigned i) const
+  {
+    return locvec[i].loc;
+  }
+
+  bool key_redundant (unsigned i) const
+  {
+    return locvec[i].key_redundant;
+  }
+
+  tag_types class_key (unsigned i) const
+  {
+    return locvec[i].class_key;
+  }
+
+  /* The location of a single mention of a class type with the given
+     class-key.  */
+  struct class_key_loc_t
+  {
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+
+    /* The function the type is mentioned in.  */
+    tree func;
+    /* The exact location.  */
+    location_t loc;
+    /* The class-key used in the mention of the type.  */
+    tag_types class_key;
+    /* True when the class-key could be omitted at this location
+       without an ambiguity with another symbol of the same name.  */
+    bool key_redundant;
+  };
+  /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
+  vec <class_key_loc_t> locvec;
+  /* LOCVEC index of the definition or UINT_MAX 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 for a class and the class_decl_loc_t
+   description above.  */
+typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t;
+static class_to_loc_map_t class2loc;
+
+/* Issue an error message if the CLASS_KEY does not match the TYPE.
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a declaration of class TYPE and clear for a reference to
+   it that is not a declaration of it.  */
+
+static void
+cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
+			   tag_types class_key, tree type, bool def_p,
+			   bool decl_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"
@@ -30613,7 +30783,228 @@ 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 (!warn_mismatched_tags && !warn_redundant_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);
+
+  /* The class-key is redundant for uses of the CLASS_TYPE that are
+     neither definitions of it nor declarations, and for which name
+     lookup returns just the type itself.  */
+  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		"redundant class-key %qs in reference to %q#T",
+		class_key == union_type ? "union"
+		: class_key == record_type ? "struct" : "class",
+		type);
+    }
+
+  if (seen_as_union || !warn_mismatched_tags)
+    return;
+
+  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
+}
+
+/* Adds TYPE_DECL to the collection of class decls.  */
+
+void
+class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
+		       bool def_p)
+{
+  class_decl_loc_t *rdl = class2loc.get (type_decl);
+  if (!rdl)
+    {
+      tree type = TREE_TYPE (type_decl);
+      if (def_p || !COMPLETE_TYPE_P (type))
+	{
+	  /* TYPE_DECL is the first declaration or definition of the type
+	     (outside precompiled headers -- see below).  Just create
+	     a new entry for it.  */
+	  class2loc.put (type_decl, class_decl_loc_t (class_key, false, def_p));
+	  return;
+	}
+
+      /* TYPE was previously defined in some unknown precompiled hdeader.
+	 Simply add a record of its definition at an unknown location and
+	 proceed below to add a reference to it at the current location.
+	 (Declarations in precompiled headers that are not definitions
+	 are ignored.)  */
+      tag_types def_key
+	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+      rdl = &class2loc.get_or_insert (type_decl);
+      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+    }
+
+  /* A prior declaration of TYPE_DECL has been seen.  */
+
+  if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
+    /* Do nothing if the class-key in this declaration matches
+       the definition.  */
+    return;
+
+  /* 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 ();
+
+  /* Append a record of this declaration to the vector.  */
+  class_decl_loc_t::class_key_loc_t
+    ckl (current_function_decl, input_location, class_key, redundant);
+  rdl->locvec.safe_push (ckl);
+
+  if (rdl->idxdef < UINT_MAX)
+    {
+      /* As a space optimization diagnose declarations of a class
+	 whose definition has been seen and purge the LOCVEC of
+	 all entries except the definition.  */
+      rdl->diag_mismatched_tags (type_decl);
+      if (rdl->idxdef)
+	{
+	  class_decl_loc_t::class_key_loc_t ent = rdl->locvec[rdl->idxdef];
+	  rdl->locvec.release ();
+	  rdl->locvec.reserve (2);
+	  rdl->locvec.safe_push (ent);
+	  rdl->idxdef = 0;
+	}
+      else
+	/* Pop the entry pushed above for this declaration.  */
+	rdl->locvec.pop ();
+    }
+}
+
+/* Issues -Wmismatched-tags for a single class.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags (tree type_decl)
+{
+  unsigned ndecls = locvec.length ();
+
+  /* Skip a declaration that consistently uses the same class-key
+     or one with just a solitary declaration (i.e., TYPE_DECL). */
+  if (def_class_key != none_type || ndecls < 2)
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
+  /* Set if a class definition for RECLOC has been seen.  */
+  bool def_p = idxdef < ndecls;
+  unsigned idxguide = def_p ? 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 (class_key (idx) == class_key (idxguide))
+    if (++idx == ndecls)
+      return;
+
+  /* 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 xpect_key = class_key (def_p ? idxguide : 0);
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+  /* Set the function declaration to print in diagnostic context.  */
+  current_function_decl = function (idx);
+
+  location_t loc = location (idx);
+  bool key_redundant_p = key_redundant (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 (loc, OPT_Wmismatched_tags,
+	      "%qT declared with a mismatched class-key %qs",
+	      type_decl, xmatchkstr);
+
+  /* Suggest how to avoid the warning for each instance since
+     the guidance may be different depending on context.  */
+  inform (loc,
+	  (key_redundant_p
+	   ? G_("remove the class-key or replace it with %qs")
+	   : G_("replace the class-key with %qs")),
+	  xpectkstr);
+
+  /* Also mention the first declaration or definition that guided
+     the decision to issue the warning above.  */
+  inform (location (idxguide),
+	  (def_p
+	   ? G_("%qT defined as %qs here")
+	   : G_("%qT first declared as %qs here")),
+	  type_decl, xpectkstr);
+
+  /* Issue warnings for the remaining inconsistent declarations.  */
+  for (unsigned i = idx + 1; i != ndecls; ++i)
+    {
+      tag_types clskey = class_key (i);
+      /* Skip over the declarations that match either the definition
+	 if one was provided or the first declaration.  */
+      if (clskey == xpect_key)
+	continue;
+
+      loc = location (i);
+      key_redundant_p = key_redundant (i);
+      /* Set the function declaration to print in diagnostic context.  */
+      current_function_decl = function (i);
+      warning_at (loc, OPT_Wmismatched_tags,
+		  "%qT declared with a mismatched class-key %qs",
+		  type_decl, xmatchkstr);
+      /* Suggest how to avoid the warning for each instance since
+	 the guidance may be different depending on context.  */
+      inform (loc,
+	      (key_redundant_p
+	       ? G_("remove the class-key or replace it with %qs")
+	       : G_("replace the class-key with %qs")),
+	      xpectkstr);
+    }
+
+  /* Restore the current function in case it was replaced above.  */
+  current_function_decl = save_func;
+}
+
+/* Issues -Wmismatched-tags for all classes.  Called at the end
+   of processing a translation unit, after declarations of all class
+   types and their uses have been recorded.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags ()
+{
+  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
+  gcc_assert (warn_mismatched_tags || class2loc.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 class_to_loc_map_t::iterator iter_t;
+  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+    {
+      tree type_decl = (*it).first;
+      class_decl_loc_t &recloc = (*it).second;
+      recloc.diag_mismatched_tags (type_decl);
+    }
+
+  class2loc.empty ();
+  /* Restore the current function.  */
+  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
@@ -43046,6 +43437,8 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+  class_decl_loc_t::diag_mismatched_tags ();
+
   the_parser = NULL;
 
   finish_translation_unit ();
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d165f31a865..817c114321a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -236,7 +236,7 @@ in the following sections.
 -Wliteral-suffix @gol
 -Wmultiple-inheritance  -Wno-init-list-lifetime @gol
 -Wnamespaces  -Wnarrowing @gol
--Wpessimizing-move  -Wredundant-move @gol
+-Wpessimizing-move  -Wredundant-move -Wredundant-tags @gol
 -Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
 -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
@@ -3323,6 +3323,21 @@ treats the return value as if it were designated by an rvalue.
 
 This warning is enabled by @option{-Wextra}.
 
+@item -Wredundant-tags @r{(C++ and Objective-C++ only)}
+@opindex Wredundant-tags
+@opindex Wno-redundant-tags
+Warn about redundant class-key and enum-key in references to class types
+and enumerated types in contexts where the key can be eliminated without
+causing an ambiguity.  For example
+
+@smallexample
+struct foo;
+struct foo *p;   // -Wredundant-tags, keyword struct can be eliminated
+
+void foo ();   // "hides" struct foo
+void bar (struct foo&);   // no warning, keyword struct cannot be eliminated
+@end smallexample
+
 @item -fext-numeric-literals @r{(C++ and Objective-C++ only)}
 @opindex fext-numeric-literals
 @opindex fno-ext-numeric-literals
@@ -3458,6 +3473,32 @@ 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.  In code that is
+intended to be portable to Windows-based compilers the warning helps prevent
+unresolved references due to the difference in the mangling of symbols
+declared with different class-keys.  The option can be used either on its
+own or in conjunction with @option{-Wredundant-tags}.
+
 @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)}
 @opindex Wmultiple-inheritance
 @opindex Wno-multiple-inheritance
diff --git a/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C
new file mode 100644
index 00000000000..89b6ba55688
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C
@@ -0,0 +1,15 @@
+/*  PR c++/61339 - add mismatch between struct and class
+    Verify that declarations that don't match definitions in precompiled
+    headers are diagnosed.
+    { dg-options "-Wall -Wmismatched-tags" } */
+
+#include "Wmismatched-tags.H"
+
+class PCHDeclaredClass;
+struct PCHDeclaredStruct;
+
+struct PCHDefinedClass;       // { dg-warning "declared with a mismatched class-key 'struct'" }
+class PCHDefinedStruct;       // { dg-warning "declared with a mismatched class-key 'class'" }
+
+class PCHDeclaredClass { };
+struct PCHDeclaredStruct { };
diff --git a/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs
new file mode 100644
index 00000000000..f4c5dc557e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs
@@ -0,0 +1,7 @@
+class PCHDeclaredClass;
+
+struct PCHDeclaredStruct;
+
+class PCHDefinedClass { };
+
+struct PCHDefinedStruct { };
\ No newline at end of file
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..36a7903234a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
@@ -0,0 +1,278 @@
+/* PR c++/61339 - add mismatch between struct and class
+   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" } */
+
+namespace Classes
+{
+class A;
+class A;
+
+struct B;
+struct B;
+
+union C;
+union C;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key 'struct'" }
+class D { };                // { dg-message "Classes::D' defined as 'class' here" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key 'class'" }
+struct E { };               // { dg-message "Classes::E' defined as 'struct' here" }
+
+class D;
+struct E;
+
+class D;
+struct E;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key" }
+
+class F;                    // { dg-message "Classes::F' first declared as 'class' here" }
+class F;
+
+struct G { };               // { dg-message "Classes::G' defined as 'struct' here" }
+}   // namespace Classes
+
+
+namespace Classes
+{
+class A;
+struct B;
+union C;
+class D;
+struct E;
+
+struct F;                   // { dg-warning "Classes::F' declared with a mismatched class-key" }
+
+struct G;
+}
+
+// Verify that the correct hint is provided, one to remove the class-key
+// when it's redundant, and one to (only) replace it with the correct one
+// when it's needed to disambiguate the reference to the class type.
+namespace RemoveOrReplace
+{
+struct Func;
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to remove" { target *-*-* } .-1 }
+
+void Func ();
+
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to replace" { target *-*-* } .-1 }
+
+class Var;
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to remove" { target *-*-* } .-1 }
+void f (struct Var*);       // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+int Var;
+
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to replace" { target *-*-* } .-1 }
+}
+
+namespace GlobalObjects
+{
+class A;                    // { dg-message "'GlobalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'GlobalObjects::B' first declared as 'struct' here" }
+class C { };                // { dg-message "'GlobalObjects::C' defined as 'class' here" }
+
+extern A a0;
+extern class A a1;
+extern class A a2;
+
+extern B b0;
+extern struct B b1;
+extern struct B b2;
+
+extern struct A a3;         // { dg-warning "GlobalObjects::A' declared with a mismatched class-key" }
+extern class A a4;
+
+extern class B b3;          // { dg-warning "GlobalObjects::B' declared with a mismatched class-key" }
+extern struct B b4;
+
+extern struct C c[];        // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+extern char
+arr[sizeof (struct C)];     // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+}   // namespace GlobalObjects
+
+
+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 (A*, B&)
+{
+  class A *a1;
+  class A *a2;
+
+  struct B *b1;
+  struct B *b2;
+
+  struct A *a3;             // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+  class A *a4;
+
+  class B *b3;              // { dg-warning "LocalObjects::B' declared with a mismatched class-key" }
+  struct B *b4;
+}
+
+void g (struct A*);         // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+
+}   // namespace LocalObjects
+
+
+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 "MemberClasses::G::H' declared with a mismatched class-key" }
+  class I { };              // { dg-message "MemberClasses::G::I' defined as 'class' here" }
+  struct I;                 // { dg-warning "MemberClasses::G::I' declared with a mismatched class-key" }
+};
+}   // namespace MemberClasses
+
+
+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 "DataMembers::G' declared with a mismatched class-key" }
+  class G *p1;
+
+  struct H &r0;
+  class H &r1;              // { dg-warning "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 "DataMembers::I::K' declared with a mismatched class-key" }
+
+  struct J j1;              // { dg-warning "DataMembers::I::J' declared with a mismatched class-key" }
+  struct K k1;
+};
+}   // namespace DataMembers
+
+
+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 "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+class D                     // { dg-message "Templates::D\[^\n\r]*' defined as 'class' here" }
+{ public: D (); };
+
+template <int> class E;     // { dg-warning "Templates::E\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+struct E                    // { dg-message "Templates::E\[^\n\r]*' defined as 'struct' here" }
+{ int i; };
+
+template <int> class D;
+template <int> struct E;
+
+template <int>
+struct D;                   // { dg-warning "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+                            // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 }
+}   // namespace Templates
+
+
+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 "ExplicitSpecializations::D\[^\n\r]*' declared with a mismatched class-key " }
+template <>
+struct D<0> { };            // { dg-message "ExplicitSpecializations::D\[^\n\r]*' defined as 'struct' here" }
+
+template <int> struct E;
+template <> struct E<0>;    // { dg-warning "ExplicitSpecializations::E\[^\n\r]*' declared with a mismatched class-key" }
+template <>
+class E<0> { };             // { dg-message "ExplicitSpecializations::E\[^\n\r]*' defined as 'class' here" }
+
+template <int> struct F;
+template <> class F<0> { }; // { dg-message "ExplicitSpecializations::F\[^\n\r]*' defined as 'class' here" }
+
+template <>
+struct F<0>;                // { dg-warning "ExplicitSpecializations::F\[^\n\r]*' declared with a mismatched class-key" }
+}   // namespace ExplicitSpecializations
+
+
+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 "PartialSpecializations::E<const T>' first declared as 'struct' here" }
+
+template <class T>
+class E<const T>;           // { dg-warning "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 "PartialSpecializations::F<const T>' declared with a mismatched class-key" }
+}   // namespace PartialSpecializations
+
+
+namespace Classes
+{
+struct G;
+
+class G;                    // { dg-warning "Classes::G' declared with a mismatched class-key 'class'" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
new file mode 100644
index 00000000000..ac5afa912a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
@@ -0,0 +1,128 @@
+/* PR c++/61339 - add mismatch between struct and class
+   Test to verify that -Wredundant-tags is issued for references to class
+   types that use the class-key even though they don't need to.
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+struct A;
+
+extern A *pa;
+extern struct A *pa;        // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+extern A aa[];
+extern struct A aa[];       // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+void func (A*);
+void func (struct A*);      // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+int A;
+
+extern struct A *pa;
+extern struct A aa[];
+void func (struct A*);
+
+
+class B;
+
+extern B *pb;
+extern class B *pb;         // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+extern B ab[];
+extern class B ab[];        // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+void func (B*);
+void func (class B*);       // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+int B;
+
+extern class B *pb;
+extern class B ab[];
+void func (class B*);
+
+
+enum C { c0 };
+
+extern C *pc;
+extern enum C *pc;          // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+extern C ac[];
+extern enum C ac[];         // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+void func (C*);
+void func (enum C*);        // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+int C;
+
+extern enum C *pc;
+extern enum C ac[];
+void func (enum C*);
+
+
+#if __cplusplus > 199711L
+
+enum class D1 { d1 };
+enum struct D2 { d2 };
+
+#else
+
+enum D1 { d1 };
+enum D2 { d2 };
+
+#endif
+
+extern D1 *pd1;
+extern D2 *pd2;
+extern enum D1 *pd1;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } .-1 }
+
+extern enum D2 *pd2;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D2'" "C++ 98" { target c++98_only } .-1 }
+
+extern D1 ad1[];
+extern D2 ad2[];
+
+#if __cplusplus > 199711L
+extern enum class D1 ad1[]; // { dg-warning "redundant enum-key 'enum class' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'class' keyword" "C++ 11 and above" { target c++11 } .-1 }
+/* The pretty printer cannot differentiate between enum class and enum struct
+   because the C++ front-end doesn't encode it so allow for both in the text
+   of the warning below.  */
+extern enum struct D2 ad2[]; // { dg-warning "redundant enum-key 'enum struct' in reference to 'enum \(class|struct\) D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'struct' keyword" "C++ 11 and above" { target c++11 } .-1 }
+#else
+extern enum D1 ad1[];       // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } }
+#endif
+
+void func (D1*);
+void func (enum D1*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+void func (D2*);
+void func (enum D2*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+int D1, D2;
+
+extern enum D1 *pd1;
+extern enum D1 ad1[];
+void func (enum D1*);
+
+extern enum D2 *pd2;
+extern enum D2 ad2[];
+void func (enum D2*);
+
+
+union U;
+
+extern U *pu;
+extern union U *pu;         // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+extern U au[];
+extern union U au[];        // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+void func (U*);
+void func (union U*);       // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+int U;
+
+extern union U *pu;
+extern union U au[];
+void func (union U*);

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-10  0:29                         ` Martin Sebor
@ 2019-12-16 16:35                           ` Martin Sebor
  2019-12-16 23:01                           ` Jason Merrill
  1 sibling, 0 replies; 47+ messages in thread
From: Martin Sebor @ 2019-12-16 16:35 UTC (permalink / raw)
  To: Jason Merrill, Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely

Ping: https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00642.html

Jason, I'm on PTO until 1/6 starting this Thursday, with no
connectivity.  If there are any further changes to make to
the patch I will need to make them before then, or otherwise
after I get back in January.

On 12/9/19 5:29 PM, Martin Sebor wrote:
> On 12/6/19 12:08 PM, Jason Merrill wrote:
>> On 12/5/19 6:47 PM, Jakub Jelinek wrote:
>>> On Thu, Dec 05, 2019 at 04:33:10PM -0700, Martin Sebor wrote:
>>>>> It's hard to distinguish between this type and the previous one by 
>>>>> name;
>>>>> this one should probably have "map" in its name.
>>>>>
>>>>>> +static GTY (()) record_to_locs_t *rec2loc;
>>>>> ...
>>>>>> +    rec2loc = new record_to_locs_t ();
>>>>>
>>>>> If this isn't GC-allocated, marking it with GTY(()) seems wrong. 
>>>>> How do
>>>>> you imagine this warning interacting with PCH?
>>>>
>>>> I have to confess I know too little about PCH to have an idea how
>>>> it might interact.  Is there something you suggest I try testing?
>>>
>>> For your patch, obviously some struct/class forward declarations or
>>> definitions in a header that you compile into PCH and then the main 
>>> testcase
>>> that contains the mismatched pairs.
>>>
>>> If there is something that you need to record during parsing of the
>>> precompiled header and use later on, everything needs to be GGC 
>>> allocated.
>>> So, the hash_map needs to be created with something like
>>> hash_map<something, something_else>::create_ggc (nnn)
>>> and it really can't use pointer hashing, but has to use some 
>>> different one
>>> (say on DECL_UID, TYPE_UID etc.), because the addresses are remapped 
>>> during
>>> PCH save/restore cycle, but hash tables aren't rehashed.
>>> See e.g. PR92458.
>>
>> Alternately you can decide that this information will not be saved to 
>> PCH, and rely on CLASSTYPE_DECLARED_CLASS for classes loaded from a PCH.
> 
> This seems like the right approach to me.  Mismatches in
> a precompiled header should be diagnosed when the header is
> being compiled, so the only ones involving its uses should
> be between classes defined in it and declared or referenced
> outside it.  I've implemented this in the attached revision.
> 
> Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-10  0:29                         ` Martin Sebor
  2019-12-16 16:35                           ` Martin Sebor
@ 2019-12-16 23:01                           ` Jason Merrill
  2019-12-16 23:36                             ` Martin Sebor
  1 sibling, 1 reply; 47+ messages in thread
From: Jason Merrill @ 2019-12-16 23:01 UTC (permalink / raw)
  To: Martin Sebor, Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely

On 12/9/19 7:29 PM, Martin Sebor wrote:

Just a few nits:

> +/* A mapping between a TYPE_DECL for a class and the class_decl_loc_t
> +   description above.  */
> +typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t;
> +static class_to_loc_map_t class2loc;

I think we can make these members of class_decl_loc_t, now that we 
aren't marking them with GTY.

> +  class_decl_loc_t *rdl = class2loc.get (type_decl);
> +  if (!rdl)
> +    {
> +      tree type = TREE_TYPE (type_decl);
> +      if (def_p || !COMPLETE_TYPE_P (type))
> +	{
> +	  /* TYPE_DECL is the first declaration or definition of the type
> +	     (outside precompiled headers -- see below).  Just create
> +	     a new entry for it.  */
> +	  class2loc.put (type_decl, class_decl_loc_t (class_key, false, def_p));
> +	  return;
> +	}
> +
> +      /* TYPE was previously defined in some unknown precompiled hdeader.
> +	 Simply add a record of its definition at an unknown location and
> +	 proceed below to add a reference to it at the current location.
> +	 (Declarations in precompiled headers that are not definitions
> +	 are ignored.)  */
> +      tag_types def_key
> +	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
> +      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
> +      rdl = &class2loc.get_or_insert (type_decl);
> +      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
> +    }

It seems that you could use get_or_insert at the top, since you always 
end up creating an element if there wasn't one already.

> +  /* A prior declaration of TYPE_DECL has been seen.  */
> +
> +  if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
> +    /* Do nothing if the class-key in this declaration matches
> +       the definition.  */
> +    return;
...

I still think that the rest of this function could be a non-static 
member function to avoid so many "rdl->".

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-16 23:01                           ` Jason Merrill
@ 2019-12-16 23:36                             ` Martin Sebor
  2019-12-17 19:41                               ` Jason Merrill
  0 siblings, 1 reply; 47+ messages in thread
From: Martin Sebor @ 2019-12-16 23:36 UTC (permalink / raw)
  To: Jason Merrill, Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely

[-- Attachment #1: Type: text/plain, Size: 2475 bytes --]

On 12/16/19 3:51 PM, Jason Merrill wrote:
> On 12/9/19 7:29 PM, Martin Sebor wrote:
> 
> Just a few nits:
> 
>> +/* A mapping between a TYPE_DECL for a class and the class_decl_loc_t
>> +   description above.  */
>> +typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t;
>> +static class_to_loc_map_t class2loc;
> 
> I think we can make these members of class_decl_loc_t, now that we 
> aren't marking them with GTY.
> 
>> +  class_decl_loc_t *rdl = class2loc.get (type_decl);
>> +  if (!rdl)
>> +    {
>> +      tree type = TREE_TYPE (type_decl);
>> +      if (def_p || !COMPLETE_TYPE_P (type))
>> +    {
>> +      /* TYPE_DECL is the first declaration or definition of the type
>> +         (outside precompiled headers -- see below).  Just create
>> +         a new entry for it.  */
>> +      class2loc.put (type_decl, class_decl_loc_t (class_key, false, 
>> def_p));
>> +      return;
>> +    }
>> +
>> +      /* TYPE was previously defined in some unknown precompiled 
>> hdeader.
>> +     Simply add a record of its definition at an unknown location and
>> +     proceed below to add a reference to it at the current location.
>> +     (Declarations in precompiled headers that are not definitions
>> +     are ignored.)  */
>> +      tag_types def_key
>> +    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
>> +      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
>> +      rdl = &class2loc.get_or_insert (type_decl);
>> +      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
>> +    }
> 
> It seems that you could use get_or_insert at the top, since you always 
> end up creating an element if there wasn't one already.
> 
>> +  /* A prior declaration of TYPE_DECL has been seen.  */
>> +
>> +  if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
>> +    /* Do nothing if the class-key in this declaration matches
>> +       the definition.  */
>> +    return;
> ...
> 
> I still think that the rest of this function could be a non-static 
> member function to avoid so many "rdl->".

Attached is another revision with these changes.

Martin

[-- Attachment #2: gcc-61339.diff --]
[-- Type: text/x-patch, Size: 39736 bytes --]

PR c++/61339 - add warning for mismatch between struct and class

gcc/c-family/ChangeLog:

	PR c++/61339
	* c.opt (-Wmismatched-tags, -Wredundant-tags): New options.

gcc/cp/ChangeLog:

	PR c++/61339
	* parser.c (cp_parser_maybe_warn_enum_key): New function.
	(class_decl_loc_t): New class.
	(cp_parser_elaborated_type_specifier): Call
	cp_parser_maybe_warn_enum_key.
	(cp_parser_class_head): Call cp_parser_check_class_key.
	(cp_parser_check_class_key): Add arguments.  Call class_decl_loc_t::add.
	(c_parse_file): Call class_decl_loc_t::diag_mismatched_tags.

gcc/testsuite/ChangeLog:

	PR c++/61339
	* g++.dg/warn/Wmismatched-tags.C: New test.
	* g++.dg/warn/Wredundant-tags.C: New test.
	* g++.dg/pch/Wmismatched-tags.C: New test.
	* g++.dg/pch/Wmismatched-tags.Hs: New test header.

gcc/ChangeLog:

	PR c++/61339
	* doc/invoke.texi (-Wmismatched-tags, -Wredundant-tags): Document
	new C++ options.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 914a2f0ef44..4f3d3cf0d43 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -755,6 +755,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 or referenced 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.
@@ -783,6 +787,10 @@ Wpacked-not-aligned
 C ObjC C++ ObjC++ Var(warn_packed_not_aligned) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn when fields in a struct with the packed attribute are misaligned.
 
+Wredundant-tags
+C++ Objc++ Var(warn_redundant_tags) Warning
+Warn when a class or enumerated type is referenced using a redundant class-key.
+
 Wsized-deallocation
 C++ ObjC++ Var(warn_sized_deallocation) Warning EnabledBy(Wextra)
 Warn about missing sized deallocation functions.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 16d1359c47d..d02a4afd736 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2599,8 +2599,9 @@ static enum tag_types cp_parser_token_is_class_key
   (cp_token *);
 static enum tag_types cp_parser_token_is_type_parameter_key
   (cp_token *);
+static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid);
 static void cp_parser_check_class_key
-  (enum tag_types, tree type);
+(cp_parser *, location_t, enum tag_types, tree type, bool, bool);
 static void cp_parser_check_access_in_redeclaration
   (tree type, location_t location);
 static bool cp_parser_optional_template_keyword
@@ -18498,6 +18499,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   tree globalscope;
   cp_token *token = NULL;
 
+  /* For class and enum types the location of the class-key or enum-key.  */
+  location_t key_loc = cp_lexer_peek_token (parser->lexer)->location;
+  /* For a scoped enum, the 'class' or 'struct' keyword id.  */
+  rid scoped_key = RID_MAX;
+
   /* See if we're looking at the `enum' keyword.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM))
     {
@@ -18508,10 +18514,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
       /* Issue a warning if the `struct' or `class' key (for C++0x scoped
 	 enums) is used here.  */
       cp_token *token = cp_lexer_peek_token (parser->lexer);
-      if (cp_parser_is_keyword (token, RID_CLASS)
-	  || cp_parser_is_keyword (token, RID_STRUCT))
+      if (cp_parser_is_keyword (token, scoped_key = RID_CLASS)
+	  || cp_parser_is_keyword (token, scoped_key = RID_STRUCT))
 	{
-	  gcc_rich_location richloc (token->location);
+	  location_t loc = token->location;
+	  gcc_rich_location richloc (loc);
 	  richloc.add_range (input_location);
 	  richloc.add_fixit_remove ();
 	  pedwarn (&richloc, 0, "elaborated-type-specifier for "
@@ -18519,7 +18526,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		   token->u.value);
 	  /* Consume the `struct' or `class' and parse it anyway.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  /* Create a combined location for the whole scoped-enum-key.  */
+	  key_loc = make_location (key_loc, key_loc, loc);
 	}
+      else
+	scoped_key = RID_MAX;
+
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
     }
@@ -18535,6 +18547,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   /* Otherwise it must be a class-key.  */
   else
     {
+      key_loc = cp_lexer_peek_token (parser->lexer)->location;
       tag_type = cp_parser_class_key (parser);
       if (tag_type == none_type)
 	return error_mark_node;
@@ -18845,13 +18858,18 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 		 "attributes ignored on elaborated-type-specifier that is not a forward declaration");
     }
 
-  if (tag_type != enum_type)
+  if (tag_type == enum_type)
+    cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
+  else
     {
+      /* Diagnose class/struct/union mismatches.  */
+      cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
+				 cp_parser_declares_only_class_p (parser));
+
       /* 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
@@ -24389,11 +24407,14 @@ cp_parser_class_head (cp_parser* parser,
 		       parser->num_template_parameter_lists);
     }
 
+  /* Diagnose class/struct/union mismatches.  */
+  cp_parser_check_class_key (parser, UNKNOWN_LOCATION, class_key, type,
+			     true, 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:
@@ -30617,14 +30638,169 @@ cp_parser_token_is_type_parameter_key (cp_token* token)
     }
 }
 
-/* Issue an error message if the CLASS_KEY does not match the TYPE.  */
+/* Diagnose redundant enum-keys.  */
+
+static void
+cp_parser_maybe_warn_enum_key (cp_parser *parser, location_t key_loc,
+			       tree type, rid scoped_key)
+{
+  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);
+
+  /* The enum-key is redundant for uses of the TYPE that are not
+     declarations and for which name lookup returns just the type
+     itself.  */
+  if (decl == type_decl)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant enum-key %<enum%s%> in reference to %q#T",
+		  (scoped_key == RID_CLASS ? " class"
+		   : scoped_key == RID_STRUCT ? " struct" : ""), type);
+    }
+}
+
+/* Describes the set of declarations of a struct, class, or class template
+   or its specializations.  Used for -Wmismatched-tags.  */
+
+class class_decl_loc_t
+{
+ public:
+
+  class_decl_loc_t ()
+    : locvec (), idxdef (), def_class_key ()
+  {
+    locvec.create (4);
+  }
+
+  /* Constructs 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 an ambiguity with
+     another symbol with the same name.
+     DEF_P is true for a class declaration that is a definition.
+     CURLOC is the associated location.  */
+  class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
+		    location_t curloc = input_location)
+    : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
+  {
+    locvec.create (4);
+    class_key_loc_t ckl (current_function_decl, curloc, class_key,
+			 key_redundant);
+    locvec.quick_push (ckl);
+  }
+
+  /* Copy, assign, and destroy the object.  Necessary because LOCVEC
+     isn't safely copyable and assignable and doesn't release storage
+     on its own.  */
+  class_decl_loc_t (const class_decl_loc_t &rhs)
+    : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef),
+      def_class_key (rhs.def_class_key)
+  { }
+
+  class_decl_loc_t& operator= (const class_decl_loc_t &rhs)
+  {
+    if (this == &rhs)
+      return *this;
+    locvec.release ();
+    locvec = rhs.locvec.copy ();
+    idxdef = rhs.idxdef;
+    def_class_key = rhs.def_class_key;
+    return *this;
+  }
+
+  ~class_decl_loc_t ()
+  {
+    locvec.release ();
+  }
+
+  /* Issues -Wmismatched-tags for a single class.  */
+  void diag_mismatched_tags (tree);
+
+  /* Issues -Wmismatched-tags for all classes.  */
+  static void diag_mismatched_tags ();
+
+  /* Adds TYPE_DECL to the collection of class decls.  */
+  static void add (tree, tag_types, bool, bool);
+
+  /* Either adds this decl to the collection of class decls
+     or diagnoses it, whichever is appropriate.  */
+  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool);
+
+private:
+
+  tree function (unsigned i) const
+  {
+    return locvec[i].func;
+  }
+
+  location_t location (unsigned i) const
+  {
+    return locvec[i].loc;
+  }
+
+  bool key_redundant (unsigned i) const
+  {
+    return locvec[i].key_redundant;
+  }
+
+  tag_types class_key (unsigned i) const
+  {
+    return locvec[i].class_key;
+  }
+
+  /* The location of a single mention of a class type with the given
+     class-key.  */
+  struct class_key_loc_t
+  {
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+
+    /* The function the type is mentioned in.  */
+    tree func;
+    /* The exact location.  */
+    location_t loc;
+    /* The class-key used in the mention of the type.  */
+    tag_types class_key;
+    /* True when the class-key could be omitted at this location
+       without an ambiguity with another symbol of the same name.  */
+    bool key_redundant;
+  };
+  /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
+  vec <class_key_loc_t> locvec;
+  /* LOCVEC index of the definition or UINT_MAX 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 for a class and the class_decl_loc_t
+     description above.  */
+  typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t;
+  static class_to_loc_map_t class2loc;
+};
+
+class_decl_loc_t::class_to_loc_map_t class_decl_loc_t::class2loc;
+
+/* Issue an error message if the CLASS_KEY does not match the TYPE.
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a declaration of class TYPE and clear for a reference to
+   it that is not a declaration of it.  */
 
 static void
-cp_parser_check_class_key (enum tag_types class_key, tree type)
+cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
+			   tag_types class_key, tree type, bool def_p,
+			   bool decl_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"
@@ -30632,7 +30808,241 @@ 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 (!warn_mismatched_tags && !warn_redundant_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);
+
+  /* The class-key is redundant for uses of the CLASS_TYPE that are
+     neither definitions of it nor declarations, and for which name
+     lookup returns just the type itself.  */
+  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		"redundant class-key %qs in reference to %q#T",
+		class_key == union_type ? "union"
+		: class_key == record_type ? "struct" : "class",
+		type);
+    }
+
+  if (seen_as_union || !warn_mismatched_tags)
+    return;
+
+  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
+}
+
+/* Adds TYPE_DECL to the collection of class decls.  */
+
+void
+class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
+		       bool def_p)
+{
+  class_decl_loc_t *rdl = class2loc.get (type_decl);
+  if (!rdl)
+    {
+      rdl = &class2loc.get_or_insert (type_decl);
+
+      tree type = TREE_TYPE (type_decl);
+      if (def_p || !COMPLETE_TYPE_P (type))
+	{
+	  /* TYPE_DECL is the first declaration or definition of the type
+	     (outside precompiled headers -- see below).  Just create
+	     a new entry for it.  */
+	  *rdl = class_decl_loc_t (class_key, false, def_p);
+	  return;
+	}
+
+      /* TYPE was previously defined in some unknown precompiled hdeader.
+	 Simply add a record of its definition at an unknown location and
+	 proceed below to add a reference to it at the current location.
+	 (Declarations in precompiled headers that are not definitions
+	 are ignored.)  */
+      tag_types def_key
+	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+    }
+
+  /* A prior declaration of TYPE_DECL has been seen.  */
+
+  if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
+    /* Do nothing if the class-key in this declaration matches
+       the definition.  */
+    return;
+
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+}
+
+/* Either adds this DECL corresponding to the TYPE_DECL to the collection
+   of class decls or diagnoses it, whichever is appropriate.  */
+
+void
+class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
+					      tag_types class_key,
+					      bool redundant,
+					      bool def_p)
+{
+  /* 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 (def_class_key != class_key)
+    def_class_key = none_type;
+
+  /* Set IDXDEF to the index of the vector corresponding to
+     the definition.  */
+  if (def_p)
+    idxdef = locvec.length ();
+
+  /* Append a record of this declaration to the vector.  */
+  class_key_loc_t ckl (current_function_decl, input_location, class_key,
+		       redundant);
+  locvec.safe_push (ckl);
+
+  if (idxdef == UINT_MAX)
+    return;
+
+  /* As a space optimization diagnose declarations of a class
+     whose definition has been seen and purge the LOCVEC of
+     all entries except the definition.  */
+  diag_mismatched_tags (type_decl);
+  if (idxdef)
+    {
+      class_decl_loc_t::class_key_loc_t ent = locvec[idxdef];
+      locvec.release ();
+      locvec.reserve (2);
+      locvec.safe_push (ent);
+      idxdef = 0;
+    }
+  else
+    /* Pop the entry pushed above for this declaration.  */
+    locvec.pop ();
+}
+
+/* Issues -Wmismatched-tags for a single class.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags (tree type_decl)
+{
+  unsigned ndecls = locvec.length ();
+
+  /* Skip a declaration that consistently uses the same class-key
+     or one with just a solitary declaration (i.e., TYPE_DECL). */
+  if (def_class_key != none_type || ndecls < 2)
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
+  /* Set if a class definition for RECLOC has been seen.  */
+  bool def_p = idxdef < ndecls;
+  unsigned idxguide = def_p ? 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 (class_key (idx) == class_key (idxguide))
+    if (++idx == ndecls)
+      return;
+
+  /* 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 xpect_key = class_key (def_p ? idxguide : 0);
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+  /* Set the function declaration to print in diagnostic context.  */
+  current_function_decl = function (idx);
+
+  location_t loc = location (idx);
+  bool key_redundant_p = key_redundant (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 (loc, OPT_Wmismatched_tags,
+	      "%qT declared with a mismatched class-key %qs",
+	      type_decl, xmatchkstr);
+
+  /* Suggest how to avoid the warning for each instance since
+     the guidance may be different depending on context.  */
+  inform (loc,
+	  (key_redundant_p
+	   ? G_("remove the class-key or replace it with %qs")
+	   : G_("replace the class-key with %qs")),
+	  xpectkstr);
+
+  /* Also point to the first declaration or definition that guided
+     the decision to issue the warning above.  */
+  inform (location (idxguide),
+	  (def_p
+	   ? G_("%qT defined as %qs here")
+	   : G_("%qT first declared as %qs here")),
+	  type_decl, xpectkstr);
+
+  /* Issue warnings for the remaining inconsistent declarations.  */
+  for (unsigned i = idx + 1; i != ndecls; ++i)
+    {
+      tag_types clskey = class_key (i);
+      /* Skip over the declarations that match either the definition
+	 if one was provided or the first declaration.  */
+      if (clskey == xpect_key)
+	continue;
+
+      loc = location (i);
+      key_redundant_p = key_redundant (i);
+      /* Set the function declaration to print in diagnostic context.  */
+      current_function_decl = function (i);
+      warning_at (loc, OPT_Wmismatched_tags,
+		  "%qT declared with a mismatched class-key %qs",
+		  type_decl, xmatchkstr);
+      /* Suggest how to avoid the warning for each instance since
+	 the guidance may be different depending on context.  */
+      inform (loc,
+	      (key_redundant_p
+	       ? G_("remove the class-key or replace it with %qs")
+	       : G_("replace the class-key with %qs")),
+	      xpectkstr);
+    }
+
+  /* Restore the current function in case it was replaced above.  */
+  current_function_decl = save_func;
+}
+
+/* Issues -Wmismatched-tags for all classes.  Called at the end
+   of processing a translation unit, after declarations of all class
+   types and their uses have been recorded.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags ()
+{
+  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
+  gcc_assert (warn_mismatched_tags || class2loc.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 class_to_loc_map_t::iterator iter_t;
+  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+    {
+      tree type_decl = (*it).first;
+      class_decl_loc_t &recloc = (*it).second;
+      recloc.diag_mismatched_tags (type_decl);
+    }
+
+  class2loc.empty ();
+  /* Restore the current function.  */
+  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
@@ -43065,6 +43475,8 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+  class_decl_loc_t::diag_mismatched_tags ();
+
   the_parser = NULL;
 
   finish_translation_unit ();
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f04e9151196..216839537de 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -236,7 +236,7 @@ in the following sections.
 -Wliteral-suffix @gol
 -Wmultiple-inheritance  -Wno-init-list-lifetime @gol
 -Wnamespaces  -Wnarrowing @gol
--Wpessimizing-move  -Wredundant-move @gol
+-Wpessimizing-move  -Wredundant-move -Wredundant-tags @gol
 -Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
 -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
@@ -3323,6 +3323,21 @@ treats the return value as if it were designated by an rvalue.
 
 This warning is enabled by @option{-Wextra}.
 
+@item -Wredundant-tags @r{(C++ and Objective-C++ only)}
+@opindex Wredundant-tags
+@opindex Wno-redundant-tags
+Warn about redundant class-key and enum-key in references to class types
+and enumerated types in contexts where the key can be eliminated without
+causing an ambiguity.  For example
+
+@smallexample
+struct foo;
+struct foo *p;   // -Wredundant-tags, keyword struct can be eliminated
+
+void foo ();   // "hides" struct foo
+void bar (struct foo&);   // no warning, keyword struct cannot be eliminated
+@end smallexample
+
 @item -fext-numeric-literals @r{(C++ and Objective-C++ only)}
 @opindex fext-numeric-literals
 @opindex fno-ext-numeric-literals
@@ -3458,6 +3473,32 @@ 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.  In code that is
+intended to be portable to Windows-based compilers the warning helps prevent
+unresolved references due to the difference in the mangling of symbols
+declared with different class-keys.  The option can be used either on its
+own or in conjunction with @option{-Wredundant-tags}.
+
 @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)}
 @opindex Wmultiple-inheritance
 @opindex Wno-multiple-inheritance
diff --git a/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C
new file mode 100644
index 00000000000..89b6ba55688
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C
@@ -0,0 +1,15 @@
+/*  PR c++/61339 - add mismatch between struct and class
+    Verify that declarations that don't match definitions in precompiled
+    headers are diagnosed.
+    { dg-options "-Wall -Wmismatched-tags" } */
+
+#include "Wmismatched-tags.H"
+
+class PCHDeclaredClass;
+struct PCHDeclaredStruct;
+
+struct PCHDefinedClass;       // { dg-warning "declared with a mismatched class-key 'struct'" }
+class PCHDefinedStruct;       // { dg-warning "declared with a mismatched class-key 'class'" }
+
+class PCHDeclaredClass { };
+struct PCHDeclaredStruct { };
diff --git a/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs
new file mode 100644
index 00000000000..f4c5dc557e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs
@@ -0,0 +1,7 @@
+class PCHDeclaredClass;
+
+struct PCHDeclaredStruct;
+
+class PCHDefinedClass { };
+
+struct PCHDefinedStruct { };
\ No newline at end of file
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..36a7903234a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
@@ -0,0 +1,278 @@
+/* PR c++/61339 - add mismatch between struct and class
+   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" } */
+
+namespace Classes
+{
+class A;
+class A;
+
+struct B;
+struct B;
+
+union C;
+union C;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key 'struct'" }
+class D { };                // { dg-message "Classes::D' defined as 'class' here" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key 'class'" }
+struct E { };               // { dg-message "Classes::E' defined as 'struct' here" }
+
+class D;
+struct E;
+
+class D;
+struct E;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key" }
+
+class F;                    // { dg-message "Classes::F' first declared as 'class' here" }
+class F;
+
+struct G { };               // { dg-message "Classes::G' defined as 'struct' here" }
+}   // namespace Classes
+
+
+namespace Classes
+{
+class A;
+struct B;
+union C;
+class D;
+struct E;
+
+struct F;                   // { dg-warning "Classes::F' declared with a mismatched class-key" }
+
+struct G;
+}
+
+// Verify that the correct hint is provided, one to remove the class-key
+// when it's redundant, and one to (only) replace it with the correct one
+// when it's needed to disambiguate the reference to the class type.
+namespace RemoveOrReplace
+{
+struct Func;
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to remove" { target *-*-* } .-1 }
+
+void Func ();
+
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to replace" { target *-*-* } .-1 }
+
+class Var;
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to remove" { target *-*-* } .-1 }
+void f (struct Var*);       // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+int Var;
+
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to replace" { target *-*-* } .-1 }
+}
+
+namespace GlobalObjects
+{
+class A;                    // { dg-message "'GlobalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'GlobalObjects::B' first declared as 'struct' here" }
+class C { };                // { dg-message "'GlobalObjects::C' defined as 'class' here" }
+
+extern A a0;
+extern class A a1;
+extern class A a2;
+
+extern B b0;
+extern struct B b1;
+extern struct B b2;
+
+extern struct A a3;         // { dg-warning "GlobalObjects::A' declared with a mismatched class-key" }
+extern class A a4;
+
+extern class B b3;          // { dg-warning "GlobalObjects::B' declared with a mismatched class-key" }
+extern struct B b4;
+
+extern struct C c[];        // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+extern char
+arr[sizeof (struct C)];     // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+}   // namespace GlobalObjects
+
+
+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 (A*, B&)
+{
+  class A *a1;
+  class A *a2;
+
+  struct B *b1;
+  struct B *b2;
+
+  struct A *a3;             // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+  class A *a4;
+
+  class B *b3;              // { dg-warning "LocalObjects::B' declared with a mismatched class-key" }
+  struct B *b4;
+}
+
+void g (struct A*);         // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+
+}   // namespace LocalObjects
+
+
+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 "MemberClasses::G::H' declared with a mismatched class-key" }
+  class I { };              // { dg-message "MemberClasses::G::I' defined as 'class' here" }
+  struct I;                 // { dg-warning "MemberClasses::G::I' declared with a mismatched class-key" }
+};
+}   // namespace MemberClasses
+
+
+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 "DataMembers::G' declared with a mismatched class-key" }
+  class G *p1;
+
+  struct H &r0;
+  class H &r1;              // { dg-warning "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 "DataMembers::I::K' declared with a mismatched class-key" }
+
+  struct J j1;              // { dg-warning "DataMembers::I::J' declared with a mismatched class-key" }
+  struct K k1;
+};
+}   // namespace DataMembers
+
+
+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 "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+class D                     // { dg-message "Templates::D\[^\n\r]*' defined as 'class' here" }
+{ public: D (); };
+
+template <int> class E;     // { dg-warning "Templates::E\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+struct E                    // { dg-message "Templates::E\[^\n\r]*' defined as 'struct' here" }
+{ int i; };
+
+template <int> class D;
+template <int> struct E;
+
+template <int>
+struct D;                   // { dg-warning "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+                            // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 }
+}   // namespace Templates
+
+
+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 "ExplicitSpecializations::D\[^\n\r]*' declared with a mismatched class-key " }
+template <>
+struct D<0> { };            // { dg-message "ExplicitSpecializations::D\[^\n\r]*' defined as 'struct' here" }
+
+template <int> struct E;
+template <> struct E<0>;    // { dg-warning "ExplicitSpecializations::E\[^\n\r]*' declared with a mismatched class-key" }
+template <>
+class E<0> { };             // { dg-message "ExplicitSpecializations::E\[^\n\r]*' defined as 'class' here" }
+
+template <int> struct F;
+template <> class F<0> { }; // { dg-message "ExplicitSpecializations::F\[^\n\r]*' defined as 'class' here" }
+
+template <>
+struct F<0>;                // { dg-warning "ExplicitSpecializations::F\[^\n\r]*' declared with a mismatched class-key" }
+}   // namespace ExplicitSpecializations
+
+
+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 "PartialSpecializations::E<const T>' first declared as 'struct' here" }
+
+template <class T>
+class E<const T>;           // { dg-warning "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 "PartialSpecializations::F<const T>' declared with a mismatched class-key" }
+}   // namespace PartialSpecializations
+
+
+namespace Classes
+{
+struct G;
+
+class G;                    // { dg-warning "Classes::G' declared with a mismatched class-key 'class'" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
new file mode 100644
index 00000000000..ac5afa912a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
@@ -0,0 +1,128 @@
+/* PR c++/61339 - add mismatch between struct and class
+   Test to verify that -Wredundant-tags is issued for references to class
+   types that use the class-key even though they don't need to.
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+struct A;
+
+extern A *pa;
+extern struct A *pa;        // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+extern A aa[];
+extern struct A aa[];       // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+void func (A*);
+void func (struct A*);      // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+int A;
+
+extern struct A *pa;
+extern struct A aa[];
+void func (struct A*);
+
+
+class B;
+
+extern B *pb;
+extern class B *pb;         // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+extern B ab[];
+extern class B ab[];        // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+void func (B*);
+void func (class B*);       // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+int B;
+
+extern class B *pb;
+extern class B ab[];
+void func (class B*);
+
+
+enum C { c0 };
+
+extern C *pc;
+extern enum C *pc;          // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+extern C ac[];
+extern enum C ac[];         // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+void func (C*);
+void func (enum C*);        // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+int C;
+
+extern enum C *pc;
+extern enum C ac[];
+void func (enum C*);
+
+
+#if __cplusplus > 199711L
+
+enum class D1 { d1 };
+enum struct D2 { d2 };
+
+#else
+
+enum D1 { d1 };
+enum D2 { d2 };
+
+#endif
+
+extern D1 *pd1;
+extern D2 *pd2;
+extern enum D1 *pd1;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } .-1 }
+
+extern enum D2 *pd2;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D2'" "C++ 98" { target c++98_only } .-1 }
+
+extern D1 ad1[];
+extern D2 ad2[];
+
+#if __cplusplus > 199711L
+extern enum class D1 ad1[]; // { dg-warning "redundant enum-key 'enum class' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'class' keyword" "C++ 11 and above" { target c++11 } .-1 }
+/* The pretty printer cannot differentiate between enum class and enum struct
+   because the C++ front-end doesn't encode it so allow for both in the text
+   of the warning below.  */
+extern enum struct D2 ad2[]; // { dg-warning "redundant enum-key 'enum struct' in reference to 'enum \(class|struct\) D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'struct' keyword" "C++ 11 and above" { target c++11 } .-1 }
+#else
+extern enum D1 ad1[];       // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } }
+#endif
+
+void func (D1*);
+void func (enum D1*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+void func (D2*);
+void func (enum D2*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+int D1, D2;
+
+extern enum D1 *pd1;
+extern enum D1 ad1[];
+void func (enum D1*);
+
+extern enum D2 *pd2;
+extern enum D2 ad2[];
+void func (enum D2*);
+
+
+union U;
+
+extern U *pu;
+extern union U *pu;         // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+extern U au[];
+extern union U au[];        // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+void func (U*);
+void func (union U*);       // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+int U;
+
+extern union U *pu;
+extern union U au[];
+void func (union U*);

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-16 23:36                             ` Martin Sebor
@ 2019-12-17 19:41                               ` Jason Merrill
  0 siblings, 0 replies; 47+ messages in thread
From: Jason Merrill @ 2019-12-17 19:41 UTC (permalink / raw)
  To: Martin Sebor, Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely

On 12/16/19 6:31 PM, Martin Sebor wrote:
> +  class_decl_loc_t *rdl = class2loc.get (type_decl);
> +  if (!rdl)
> +    {
> +      rdl = &class2loc.get_or_insert (type_decl);

I was thinking

class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl);

OK with that change.

Jason

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2019-12-03 21:49               ` [PATCH] add " Martin Sebor
  2019-12-04 23:37                 ` Jason Merrill
@ 2020-02-18  8:42                 ` Stephan Bergmann
  2020-02-18 15:57                   ` Martin Sebor
  1 sibling, 1 reply; 47+ messages in thread
From: Stephan Bergmann @ 2020-02-18  8:42 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, gcc-patches, Jonathan Wakely

On 03/12/2019 22:49, Martin Sebor wrote:
> I added another warning: -Wredundant-tags to point out instances
> where the class-key or enum-key can safely be dropped. Both warnings
> are off by default.

I think -Wredundant-tags would be more useful if it did not warn about 
occurrences within extern "C" (at least not within included files), as a 
heuristic to filter out code that is shared between C and C++ and where 
the tags are thus necessary.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] add -Wmismatched-tags (PR 61339)
  2020-02-18  8:42                 ` Stephan Bergmann
@ 2020-02-18 15:57                   ` Martin Sebor
  0 siblings, 0 replies; 47+ messages in thread
From: Martin Sebor @ 2020-02-18 15:57 UTC (permalink / raw)
  To: Stephan Bergmann; +Cc: Jason Merrill, gcc-patches, Jonathan Wakely

On 2/18/20 1:41 AM, Stephan Bergmann wrote:
> On 03/12/2019 22:49, Martin Sebor wrote:
>> I added another warning: -Wredundant-tags to point out instances
>> where the class-key or enum-key can safely be dropped. Both warnings
>> are off by default.
> 
> I think -Wredundant-tags would be more useful if it did not warn about 
> occurrences within extern "C" (at least not within included files), as a 
> heuristic to filter out code that is shared between C and C++ and where 
> the tags are thus necessary.

I agree.  Exempting C code in headers does seem like the right thing
to do.  I opened bug 93804 to remember to get back to it for GCC 11.

Thanks
Martin

^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2020-02-18 15:57 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-08 21:58 [PATCH 0/3] add support for POD struct convention (PR 61339) Martin Sebor
2019-07-08 21:59 ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags " Martin Sebor
2019-07-22 16:39   ` Martin Sebor
2019-08-01 18:09     ` Jason Merrill
2019-08-01 23:35       ` Martin Sebor
2019-08-05 19:25         ` Jason Merrill
2019-08-05 21:56           ` Martin Sebor
2019-08-05 22:43             ` Jason Merrill
2019-12-03 21:49               ` [PATCH] add " Martin Sebor
2019-12-04 23:37                 ` Jason Merrill
2019-12-05 23:33                   ` Martin Sebor
2019-12-05 23:47                     ` Jakub Jelinek
2019-12-06 19:08                       ` Jason Merrill
2019-12-10  0:29                         ` Martin Sebor
2019-12-16 16:35                           ` Martin Sebor
2019-12-16 23:01                           ` Jason Merrill
2019-12-16 23:36                             ` Martin Sebor
2019-12-17 19:41                               ` Jason Merrill
2020-02-18  8:42                 ` Stephan Bergmann
2020-02-18 15:57                   ` Martin Sebor
2019-07-22 22:37   ` [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, " Jeff Law
2019-07-23  0:00     ` Mike Stump
2019-07-23  1:34     ` Martin Sebor
2019-07-24 17:43       ` Jeff Law
2019-07-24 19:30   ` Jeff Law
2019-07-08 22:02 ` [PATCH 2/3] change class-key of PODs to struct and others to class " Martin Sebor
2019-07-08 22:20   ` Martin Sebor
2019-07-09 14:11     ` Richard Sandiford
2019-07-09 16:37       ` Martin Sebor
2019-07-08 22:04 ` [PATCH 3/3] " Martin Sebor
2019-07-09 15:19   ` Richard Sandiford
2019-07-09 18:53     ` Martin Sebor
2019-07-10  9:40       ` Richard Biener
2019-07-10 10:52         ` Richard Sandiford
2019-07-12  8:41 ` [PATCH 0/3] add support for POD struct convention " Jakub Jelinek
2019-07-12 11:44   ` Jonathan Wakely
2019-07-12 15:14     ` Jason Merrill
2019-07-12 15:26     ` Martin Sebor
2019-07-12 15:36       ` Jonathan Wakely
2019-07-12 16:29         ` Martin Sebor
2019-07-12 15:40       ` Jason Merrill
2019-07-12 16:49         ` Martin Sebor
2019-08-14 18:50   ` Pedro Alves
2019-08-19 19:15     ` Jason Merrill
2019-07-23 16:20 ` Arvind Sankar
2019-07-23 16:42   ` Martin Sebor
2019-07-23 16:54     ` Arvind Sankar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).