This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


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

[PATCH 8/9] class-local typedef substitutions


This patch implements class-local typedef substitutions for the C++
type printer.  That is, when printing a type, gdb will now look at
template argument names (if any) and typedefs defined in the class
body, and substitute those names when printing type names.

This generally makes the output more readable.  The '/r' flag to ptype
can be used to disable this transform for those cases where one wants
to see the raw type.

Unfortunately DWARF does not tell us whether a type originally came
from a template argument or not.  So in a case like this:

    template<typename T> class X {
      int a;
      T b;
    };

... if you display the type X<int>, both fields will appear to be of
type T.

I'm considering filing a GCC and/or DWARF feature request to fix this;
but meanwhile I think this isn't very serious, and the substitutions
are usually preferable.

Built and regtested on x86-64 F16.

	* NEWS: Update.
	* c-typeprint.c (find_typedef_for_canonicalize,
	print_name_maybe_canonical): New functions.
	(c_print_type): Look up type name.
	(cp_type_print_derivation_info): Add flags argument.  Use
	print_name_maybe_canonical.
	(cp_type_print_method_args): Add wrapping.
	(c_type_print_varspec_prefix): Use print_name_maybe_canonical.
	(c_type_print_template_args): New function.
	(c_type_print_base): Change wrapping.
	<TYPE_CODE_STRUCT>: Possibly create a typedef hash, and do
	type name lookups.
	* gdbtypes.c (types_equal): No longer static.
	* gdbtypes.h (types_equal): Declare.
	* typeprint.c (type_print_raw_options, default_ptype_flags):
	Update.
	(struct typedef_hash_table): New.
	(hash_typedef_field, eq_typedef_field,
	recursively_update_typedef_hash, add_template_parameters,
	create_typedef_hash, free_typedef_hash, do_free_typedef_hash,
	make_cleanup_free_typedef_hash, copy_typedef_hash_element,
	copy_typedef_hash, find_typedef_in_hash): New functions.
	* typeprint.h (struct type_print_options) <local_typedefs>:
	New field.
	(recursively_update_typedef_hash, add_template_parameters,
	create_typedef_hash, free_typedef_hash,
	make_cleanup_free_typedef_hash, copy_typedef_hash,
	find_typedef_in_hash): Declare.

	* gdb.cp/ptype-flags.cc: New file.
	* gdb.cp/ptype-flags.exp: New file.
	* gdb.cp/templates.exp: Use ptype/r.
	(test_ptype_of_templates, test_template_typedef): Likewise.
	* lib/cp-support.exp (cp_test_ptype_class): Add in_ptype_arg
	argument.  Handle template names and template parameters.
---
 gdb/NEWS                             |   15 +
 gdb/c-typeprint.c                    |  617 +++++++++++++++++++++-------------
 gdb/gdbtypes.c                       |    2 +-
 gdb/gdbtypes.h                       |    2 +
 gdb/testsuite/gdb.cp/ptype-flags.cc  |   47 +++
 gdb/testsuite/gdb.cp/ptype-flags.exp |   85 +++++
 gdb/testsuite/gdb.cp/templates.exp   |   46 ++--
 gdb/testsuite/lib/cp-support.exp     |   17 +-
 gdb/typeprint.c                      |  196 +++++++++++-
 gdb/typeprint.h                      |   21 ++
 10 files changed, 785 insertions(+), 263 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/ptype-flags.cc
 create mode 100644 gdb/testsuite/gdb.cp/ptype-flags.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index dba6937..e625e25 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,9 @@
 
 *** Changes since GDB 7.5
 
+* The 'ptype' and 'whatis' commands now accept an argument to control
+  type formatting.
+
 * Python scripting
 
   ** Vectors can be created with gdb.Type.vector.
@@ -39,6 +42,18 @@ pi [command]
 py [command]
   "py" is a new alias for "python".
 
+* New options
+
+set print type methods (on|off)
+show print type methods
+  Control whether method declarations are displayed by "ptype".
+  The default is to show them.
+
+set print type typedefs (on|off)
+show print type typedefs
+  Control whether typedef definitions are displayed by "ptype".
+  The default is to show them.
+
 * MI changes
 
   ** Command parameter changes are now notified using new async record
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 371f002..4fe5059 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -34,6 +34,7 @@
 #include "jv-lang.h"
 #include "gdb_string.h"
 #include <errno.h>
+#include "cp-support.h"
 
 static void c_type_print_varspec_prefix (struct type *,
 					 struct ui_file *,
@@ -45,6 +46,37 @@ static void c_type_print_modifier (struct type *,
 				   struct ui_file *,
 				   int, int);
 
+
+/* A callback function for cp_canonicalize_string_full that uses
+   find_typedef_in_hash.  */
+
+static const char *
+find_typedef_for_canonicalize (struct type *t, void *data)
+{
+  return find_typedef_in_hash (data, t);
+}
+
+/* Print NAME on STREAM.  If the 'local_typedefs' field of FLAGS is
+   not NULL, canonicalize NAME using the local typedefs first.  */
+
+static void
+print_name_maybe_canonical (const char *name,
+			    const struct type_print_options *flags,
+			    struct ui_file *stream)
+{
+  char *s = NULL;
+
+  if (flags->local_typedefs != NULL)
+    s = cp_canonicalize_string_full (name,
+				     find_typedef_for_canonicalize,
+				     (void *) flags);
+
+  fputs_filtered (s ? s : name, stream);
+  xfree (s);
+}
+
+
+
 /* LEVEL is the depth to indent lines by.  */
 
 void
@@ -57,26 +89,37 @@ c_print_type (struct type *type,
   enum type_code code;
   int demangled_args;
   int need_post_space;
+  const char *local_name;
 
   if (show > 0)
     CHECK_TYPEDEF (type);
 
-  c_type_print_base (type, stream, show, level, flags);
-  code = TYPE_CODE (type);
-  if ((varstring != NULL && *varstring != '\0')
-  /* Need a space if going to print stars or brackets;
-     but not if we will print just a type name.  */
-      || ((show > 0 || TYPE_NAME (type) == 0)
-	  && (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC
-	      || code == TYPE_CODE_METHOD
-	      || code == TYPE_CODE_ARRAY
-	      || code == TYPE_CODE_MEMBERPTR
-	      || code == TYPE_CODE_METHODPTR
-	      || code == TYPE_CODE_REF)))
-    fputs_filtered (" ", stream);
-  need_post_space = (varstring != NULL && strcmp (varstring, "") != 0);
-  c_type_print_varspec_prefix (type, stream, show, 0, need_post_space,
-			       flags);
+  local_name = find_typedef_in_hash (flags, type);
+  if (local_name != NULL)
+    {
+      fputs_filtered (local_name, stream);
+      if (varstring != NULL && *varstring != '\0')
+	fputs_filtered (" ", stream);
+    }
+  else
+    {
+      c_type_print_base (type, stream, show, level, flags);
+      code = TYPE_CODE (type);
+      if ((varstring != NULL && *varstring != '\0')
+	  /* Need a space if going to print stars or brackets;
+	     but not if we will print just a type name.  */
+	  || ((show > 0 || TYPE_NAME (type) == 0)
+	      && (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC
+		  || code == TYPE_CODE_METHOD
+		  || code == TYPE_CODE_ARRAY
+		  || code == TYPE_CODE_MEMBERPTR
+		  || code == TYPE_CODE_METHODPTR
+		  || code == TYPE_CODE_REF)))
+	fputs_filtered (" ", stream);
+      need_post_space = (varstring != NULL && strcmp (varstring, "") != 0);
+      c_type_print_varspec_prefix (type, stream, show, 0, need_post_space,
+				   flags);
+    }
 
   if (varstring != NULL)
     {
@@ -84,10 +127,13 @@ c_print_type (struct type *type,
 
       /* For demangled function names, we have the arglist as part of
          the name, so don't print an additional pair of ()'s.  */
-
-      demangled_args = strchr (varstring, '(') != NULL;
-      c_type_print_varspec_suffix (type, stream, show,
-				   0, demangled_args, flags);
+      if (local_name == NULL)
+	{
+	  demangled_args = strchr (varstring, '(') != NULL;
+	  c_type_print_varspec_suffix (type, stream, show,
+				       0, demangled_args,
+				       flags);
+	}
     }
 }
 
@@ -138,13 +184,15 @@ c_print_typedef (struct type *type,
 
 static void
 cp_type_print_derivation_info (struct ui_file *stream,
-			       struct type *type)
+			       struct type *type,
+			       const struct type_print_options *flags)
 {
   const char *name;
   int i;
 
   for (i = 0; i < TYPE_N_BASECLASSES (type); i++)
     {
+      wrap_here ("        ");
       fputs_filtered (i == 0 ? ": " : ", ", stream);
       fprintf_filtered (stream, "%s%s ",
 			BASETYPE_VIA_PUBLIC (type, i)
@@ -152,7 +200,10 @@ cp_type_print_derivation_info (struct ui_file *stream,
 				      ? "protected" : "private"),
 			BASETYPE_VIA_VIRTUAL (type, i) ? " virtual" : "");
       name = type_name_no_tag (TYPE_BASECLASS (type, i));
-      fprintf_filtered (stream, "%s", name ? name : "(null)");
+      if (name)
+	print_name_maybe_canonical (name, flags, stream);
+      else
+	fprintf_filtered (stream, "(null)");
     }
   if (i > 0)
     {
@@ -190,7 +241,10 @@ cp_type_print_method_args (struct type *mtype, const char *prefix,
 	  if (i == nargs && varargs)
 	    fprintf_filtered (stream, ", ...");
 	  else if (i < nargs)
-	    fprintf_filtered (stream, ", ");
+	    {
+	      fprintf_filtered (stream, ", ");
+	      wrap_here ("        ");
+	    }
 	}
     }
   else if (varargs)
@@ -262,7 +316,7 @@ c_type_print_varspec_prefix (struct type *type,
 				   stream, show, 0, 0, flags);
       name = type_name_no_tag (TYPE_DOMAIN_TYPE (type));
       if (name)
-	fputs_filtered (name, stream);
+	print_name_maybe_canonical (name, flags, stream);
       else
 	c_type_print_base (TYPE_DOMAIN_TYPE (type),
 			   stream, -1, passed_a_ptr, flags);
@@ -275,7 +329,7 @@ c_type_print_varspec_prefix (struct type *type,
       fprintf_filtered (stream, "(");
       name = type_name_no_tag (TYPE_DOMAIN_TYPE (type));
       if (name)
-	fputs_filtered (name, stream);
+	print_name_maybe_canonical (name, flags, stream);
       else
 	c_type_print_base (TYPE_DOMAIN_TYPE (type),
 			   stream, -1, passed_a_ptr, flags);
@@ -694,6 +748,56 @@ c_type_print_varspec_suffix (struct type *type,
     }
 }
 
+/* A helper for c_type_print_base that displays template
+   parameters and their bindings, if needed.
+
+   TABLE is the local bindings table to use.  If NULL, no printing is
+   done.  Note that, at this point, TABLE won't have any useful
+   information in it -- but it is also used as a flag to
+   print_name_maybe_canonical to activate searching the global typedef
+   table.
+
+   TYPE is the type whose template arguments are being displayed.
+
+   STREAM is the stream on which to print.  */
+
+static void
+c_type_print_template_args (const struct type_print_options *flags,
+			    struct type *type, struct ui_file *stream)
+{
+  int first = 1, i;
+
+  if (flags->raw)
+    return;
+
+  for (i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (type); ++i)
+    {
+      struct symbol *sym = TYPE_TEMPLATE_ARGUMENT (type, i);
+
+      if (SYMBOL_CLASS (sym) != LOC_TYPEDEF)
+	continue;
+
+      if (first)
+	{
+	  wrap_here ("    ");
+	  fprintf_filtered (stream, _("[with %s = "),
+			    SYMBOL_LINKAGE_NAME (sym));
+	  first = 0;
+	}
+      else
+	{
+	  fputs_filtered (", ", stream);
+	  wrap_here ("         ");
+	  fprintf_filtered (stream, "%s = ", SYMBOL_LINKAGE_NAME (sym));
+	}
+
+      c_print_type (SYMBOL_TYPE (sym), "", stream, -1, 0, flags);
+    }
+
+  if (!first)
+    fputs_filtered (_("] "), stream);
+}
+
 /* Print the name of the type (or the ultimate pointer target,
    function value or array element), or the description of a structure
    or union.
@@ -728,7 +832,6 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 
   QUIT;
 
-  wrap_here ("    ");
   if (type == NULL)
     {
       fputs_filtered (_("<type unknown>"), stream);
@@ -746,7 +849,7 @@ c_type_print_base (struct type *type, struct ui_file *stream,
       && TYPE_NAME (type) != NULL)
     {
       c_type_print_modifier (type, stream, 0, 1);
-      fputs_filtered (TYPE_NAME (type), stream);
+      print_name_maybe_canonical (TYPE_NAME (type), flags, stream);
       return;
     }
 
@@ -775,193 +878,234 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 
     case TYPE_CODE_STRUCT:
     case TYPE_CODE_UNION:
-      c_type_print_modifier (type, stream, 0, 1);
-      if (TYPE_CODE (type) == TYPE_CODE_UNION)
-	fprintf_filtered (stream, "union ");
-      else if (TYPE_DECLARED_CLASS (type))
-	fprintf_filtered (stream, "class ");
-      else
-	fprintf_filtered (stream, "struct ");
-
-      /* Print the tag if it exists.  The HP aCC compiler emits a
-         spurious "{unnamed struct}"/"{unnamed union}"/"{unnamed
-         enum}" tag for unnamed struct/union/enum's, which we don't
-         want to print.  */
-      if (TYPE_TAG_NAME (type) != NULL
-	  && strncmp (TYPE_TAG_NAME (type), "{unnamed", 8))
-	{
-	  fputs_filtered (TYPE_TAG_NAME (type), stream);
-	  if (show > 0)
-	    fputs_filtered (" ", stream);
-	}
-      wrap_here ("    ");
-      if (show < 0)
-	{
-	  /* If we just printed a tag name, no need to print anything
-	     else.  */
-	  if (TYPE_TAG_NAME (type) == NULL)
-	    fprintf_filtered (stream, "{...}");
-	}
-      else if (show > 0 || TYPE_TAG_NAME (type) == NULL)
-	{
-	  struct type *basetype;
-	  int vptr_fieldno;
-
-	  cp_type_print_derivation_info (stream, type);
-
-	  fprintf_filtered (stream, "{\n");
-	  if (TYPE_NFIELDS (type) == 0 && TYPE_NFN_FIELDS (type) == 0
-	      && TYPE_TYPEDEF_FIELD_COUNT (type) == 0)
-	    {
-	      if (TYPE_STUB (type))
-		fprintfi_filtered (level + 4, stream,
-				   _("<incomplete type>\n"));
-	      else
-		fprintfi_filtered (level + 4, stream,
-				   _("<no data fields>\n"));
-	    }
-
-	  /* Start off with no specific section type, so we can print
-	     one for the first field we find, and use that section type
-	     thereafter until we find another type.  */
-
-	  section_type = s_none;
-
-	  /* For a class, if all members are private, there's no need
-	     for a "private:" label; similarly, for a struct or union
-	     masquerading as a class, if all members are public, there's
-	     no need for a "public:" label.  */
-
-	  if (TYPE_DECLARED_CLASS (type))
-	    {
-	      QUIT;
-	      len = TYPE_NFIELDS (type);
-	      for (i = TYPE_N_BASECLASSES (type); i < len; i++)
-		if (!TYPE_FIELD_PRIVATE (type, i))
-		  {
-		    need_access_label = 1;
-		    break;
-		  }
-	      QUIT;
-	      if (!need_access_label)
-		{
-		  len2 = TYPE_NFN_FIELDS (type);
-		  for (j = 0; j < len2; j++)
+      {
+	struct type_print_options local_flags = *flags;
+	struct type_print_options semi_local_flags = *flags;
+	struct cleanup *local_cleanups = make_cleanup (null_cleanup, NULL);
+
+	local_flags.local_typedefs = NULL;
+	semi_local_flags.local_typedefs = NULL;
+
+	if (!flags->raw)
+	  {
+	    if (flags->local_typedefs)
+	      local_flags.local_typedefs
+		= copy_typedef_hash (flags->local_typedefs);
+	    else
+	      local_flags.local_typedefs = create_typedef_hash ();
+
+	    make_cleanup_free_typedef_hash (local_flags.local_typedefs);
+	  }
+
+	c_type_print_modifier (type, stream, 0, 1);
+	if (TYPE_CODE (type) == TYPE_CODE_UNION)
+	  fprintf_filtered (stream, "union ");
+	else if (TYPE_DECLARED_CLASS (type))
+	  fprintf_filtered (stream, "class ");
+	else
+	  fprintf_filtered (stream, "struct ");
+
+	/* Print the tag if it exists.  The HP aCC compiler emits a
+	   spurious "{unnamed struct}"/"{unnamed union}"/"{unnamed
+	   enum}" tag for unnamed struct/union/enum's, which we don't
+	   want to print.  */
+	if (TYPE_TAG_NAME (type) != NULL
+	    && strncmp (TYPE_TAG_NAME (type), "{unnamed", 8))
+	  {
+	    /* Passing '&local_flags' here is somewhat obscure.  We
+	       only want to do name canonicalization for the class tag
+	       using the global and enclosing typedefs (there aren't
+	       actually any local ones yet), and if the user hasn't
+	       requested raw printing.  Passing the local flags
+	       achieves this, due to how they are initialized.  */
+	    print_name_maybe_canonical (TYPE_TAG_NAME (type), &local_flags,
+					stream);
+	    if (show > 0)
+	      fputs_filtered (" ", stream);
+	  }
+
+	if (show < 0)
+	  {
+	    /* If we just printed a tag name, no need to print anything
+	       else.  */
+	    if (TYPE_TAG_NAME (type) == NULL)
+	      fprintf_filtered (stream, "{...}");
+	  }
+	else if (show > 0 || TYPE_TAG_NAME (type) == NULL)
+	  {
+	    struct type *basetype;
+	    int vptr_fieldno;
+
+	    c_type_print_template_args (&local_flags, type, stream);
+
+	    /* Add in template parameters when printing derivation info.  */
+	    add_template_parameters (local_flags.local_typedefs, type);
+	    cp_type_print_derivation_info (stream, type, &local_flags);
+
+	    /* This holds just the global typedefs and the template
+	       parameters.  */
+	    semi_local_flags.local_typedefs
+	      = copy_typedef_hash (local_flags.local_typedefs);
+	    if (semi_local_flags.local_typedefs)
+	      make_cleanup_free_typedef_hash (semi_local_flags.local_typedefs);
+
+	    /* Now add in the local typedefs.  */
+	    recursively_update_typedef_hash (local_flags.local_typedefs, type);
+
+	    fprintf_filtered (stream, "{\n");
+	    if (TYPE_NFIELDS (type) == 0 && TYPE_NFN_FIELDS (type) == 0
+		&& TYPE_TYPEDEF_FIELD_COUNT (type) == 0)
+	      {
+		if (TYPE_STUB (type))
+		  fprintfi_filtered (level + 4, stream,
+				     _("<incomplete type>\n"));
+		else
+		  fprintfi_filtered (level + 4, stream,
+				     _("<no data fields>\n"));
+	      }
+
+	    /* Start off with no specific section type, so we can print
+	       one for the first field we find, and use that section type
+	       thereafter until we find another type.  */
+
+	    section_type = s_none;
+
+	    /* For a class, if all members are private, there's no need
+	       for a "private:" label; similarly, for a struct or union
+	       masquerading as a class, if all members are public, there's
+	       no need for a "public:" label.  */
+
+	    if (TYPE_DECLARED_CLASS (type))
+	      {
+		QUIT;
+		len = TYPE_NFIELDS (type);
+		for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+		  if (!TYPE_FIELD_PRIVATE (type, i))
 		    {
-		      len = TYPE_FN_FIELDLIST_LENGTH (type, j);
-		      for (i = 0; i < len; i++)
-			if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type,
-									j), i))
-			  {
-			    need_access_label = 1;
-			    break;
-			  }
-		      if (need_access_label)
-			break;
+		      need_access_label = 1;
+		      break;
 		    }
-		}
-	    }
-	  else
-	    {
-	      QUIT;
-	      len = TYPE_NFIELDS (type);
-	      for (i = TYPE_N_BASECLASSES (type); i < len; i++)
-		if (TYPE_FIELD_PRIVATE (type, i)
-		    || TYPE_FIELD_PROTECTED (type, i))
+		QUIT;
+		if (!need_access_label)
 		  {
-		    need_access_label = 1;
-		    break;
+		    len2 = TYPE_NFN_FIELDS (type);
+		    for (j = 0; j < len2; j++)
+		      {
+			len = TYPE_FN_FIELDLIST_LENGTH (type, j);
+			for (i = 0; i < len; i++)
+			  if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type,
+									  j), i))
+			    {
+			      need_access_label = 1;
+			      break;
+			    }
+			if (need_access_label)
+			  break;
+		      }
 		  }
-	      QUIT;
-	      if (!need_access_label)
-		{
-		  len2 = TYPE_NFN_FIELDS (type);
-		  for (j = 0; j < len2; j++)
+	      }
+	    else
+	      {
+		QUIT;
+		len = TYPE_NFIELDS (type);
+		for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+		  if (TYPE_FIELD_PRIVATE (type, i)
+		      || TYPE_FIELD_PROTECTED (type, i))
 		    {
-		      QUIT;
-		      len = TYPE_FN_FIELDLIST_LENGTH (type, j);
-		      for (i = 0; i < len; i++)
-			if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type,
-									 j), i)
-			    || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type,
-									  j),
-						      i))
-			  {
-			    need_access_label = 1;
-			    break;
-			  }
-		      if (need_access_label)
-			break;
+		      need_access_label = 1;
+		      break;
 		    }
-		}
-	    }
+		QUIT;
+		if (!need_access_label)
+		  {
+		    len2 = TYPE_NFN_FIELDS (type);
+		    for (j = 0; j < len2; j++)
+		      {
+			QUIT;
+			len = TYPE_FN_FIELDLIST_LENGTH (type, j);
+			for (i = 0; i < len; i++)
+			  if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type,
+									   j), i)
+			      || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type,
+									    j),
+							i))
+			    {
+			      need_access_label = 1;
+			      break;
+			    }
+			if (need_access_label)
+			  break;
+		      }
+		  }
+	      }
 
-	  /* If there is a base class for this type,
-	     do not print the field that it occupies.  */
+	    /* If there is a base class for this type,
+	       do not print the field that it occupies.  */
 
-	  len = TYPE_NFIELDS (type);
-	  vptr_fieldno = get_vptr_fieldno (type, &basetype);
-	  for (i = TYPE_N_BASECLASSES (type); i < len; i++)
-	    {
-	      QUIT;
+	    len = TYPE_NFIELDS (type);
+	    vptr_fieldno = get_vptr_fieldno (type, &basetype);
+	    for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+	      {
+		QUIT;
 
-	      /* If we have a virtual table pointer, omit it.  Even if
-		 virtual table pointers are not specifically marked in
-		 the debug info, they should be artificial.  */
-	      if ((i == vptr_fieldno && type == basetype)
-		  || TYPE_FIELD_ARTIFICIAL (type, i))
-		continue;
+		/* If we have a virtual table pointer, omit it.  Even if
+		   virtual table pointers are not specifically marked in
+		   the debug info, they should be artificial.  */
+		if ((i == vptr_fieldno && type == basetype)
+		    || TYPE_FIELD_ARTIFICIAL (type, i))
+		  continue;
 
-	      if (need_access_label)
-		{
-		  if (TYPE_FIELD_PROTECTED (type, i))
-		    {
-		      if (section_type != s_protected)
-			{
-			  section_type = s_protected;
-			  fprintfi_filtered (level + 2, stream,
-					     "protected:\n");
-			}
-		    }
-		  else if (TYPE_FIELD_PRIVATE (type, i))
-		    {
-		      if (section_type != s_private)
-			{
-			  section_type = s_private;
-			  fprintfi_filtered (level + 2, stream,
-					     "private:\n");
-			}
-		    }
-		  else
-		    {
-		      if (section_type != s_public)
-			{
-			  section_type = s_public;
-			  fprintfi_filtered (level + 2, stream,
-					     "public:\n");
-			}
-		    }
-		}
+		if (need_access_label)
+		  {
+		    if (TYPE_FIELD_PROTECTED (type, i))
+		      {
+			if (section_type != s_protected)
+			  {
+			    section_type = s_protected;
+			    fprintfi_filtered (level + 2, stream,
+					       "protected:\n");
+			  }
+		      }
+		    else if (TYPE_FIELD_PRIVATE (type, i))
+		      {
+			if (section_type != s_private)
+			  {
+			    section_type = s_private;
+			    fprintfi_filtered (level + 2, stream,
+					       "private:\n");
+			  }
+		      }
+		    else
+		      {
+			if (section_type != s_public)
+			  {
+			    section_type = s_public;
+			    fprintfi_filtered (level + 2, stream,
+					       "public:\n");
+			  }
+		      }
+		  }
 
-	      print_spaces_filtered (level + 4, stream);
-	      if (field_is_static (&TYPE_FIELD (type, i)))
-		fprintf_filtered (stream, "static ");
-	      c_print_type (TYPE_FIELD_TYPE (type, i),
-			    TYPE_FIELD_NAME (type, i),
-			    stream, show - 1, level + 4, flags);
-	      if (!field_is_static (&TYPE_FIELD (type, i))
-		  && TYPE_FIELD_PACKED (type, i))
-		{
-		  /* It is a bitfield.  This code does not attempt
-		     to look at the bitpos and reconstruct filler,
-		     unnamed fields.  This would lead to misleading
-		     results if the compiler does not put out fields
-		     for such things (I don't know what it does).  */
-		  fprintf_filtered (stream, " : %d",
-				    TYPE_FIELD_BITSIZE (type, i));
-		}
-	      fprintf_filtered (stream, ";\n");
-	    }
+		print_spaces_filtered (level + 4, stream);
+		if (field_is_static (&TYPE_FIELD (type, i)))
+		  fprintf_filtered (stream, "static ");
+		c_print_type (TYPE_FIELD_TYPE (type, i),
+			      TYPE_FIELD_NAME (type, i),
+			      stream, show - 1, level + 4,
+			      &local_flags);
+		if (!field_is_static (&TYPE_FIELD (type, i))
+		    && TYPE_FIELD_PACKED (type, i))
+		  {
+		    /* It is a bitfield.  This code does not attempt
+		       to look at the bitpos and reconstruct filler,
+		       unnamed fields.  This would lead to misleading
+		       results if the compiler does not put out fields
+		       for such things (I don't know what it does).  */
+		    fprintf_filtered (stream, " : %d",
+				      TYPE_FIELD_BITSIZE (type, i));
+		  }
+		fprintf_filtered (stream, ";\n");
+	      }
 
 	  /* If there are both fields and methods, put a blank line
 	     between them.  Make sure to count only method that we
@@ -1059,7 +1203,8 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 			   && !is_type_conversion_operator (type, i, j))
 		    {
 		      c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)),
-				    "", stream, -1, 0, flags);
+				    "", stream, -1, 0,
+				    &local_flags);
 		      fputs_filtered (" ", stream);
 		    }
 		  if (TYPE_FN_FIELD_STUB (f, j))
@@ -1080,10 +1225,10 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 		  if (demangled_name == NULL)
 		    {
 		      /* In some cases (for instance with the HP
-		         demangling), if a function has more than 10
-		         arguments, the demangling will fail.
-		         Let's try to reconstruct the function
-		         signature from the symbol information.  */
+			 demangling), if a function has more than 10
+			 arguments, the demangling will fail.
+			 Let's try to reconstruct the function
+			 signature from the symbol information.  */
 		      if (!TYPE_FN_FIELD_STUB (f, j))
 			{
 			  int staticp = TYPE_FN_FIELD_STATIC_P (f, j);
@@ -1093,7 +1238,7 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 						     "",
 						     method_name,
 						     staticp,
-						     stream, flags);
+						     stream, &local_flags);
 			}
 		      else
 			fprintf_filtered (stream,
@@ -1140,30 +1285,40 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 	      if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0)
 		fprintf_filtered (stream, "\n");
 
-	      for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++)
-		{
-		  struct type *target = TYPE_TYPEDEF_FIELD_TYPE (type, i);
+		for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++)
+		  {
+		    struct type *target = TYPE_TYPEDEF_FIELD_TYPE (type, i);
+		    struct typedef_hash_table *table2;
+
+		    /* Dereference the typedef declaration itself.  */
+		    gdb_assert (TYPE_CODE (target) == TYPE_CODE_TYPEDEF);
+		    target = TYPE_TARGET_TYPE (target);
+
+		    print_spaces_filtered (level + 4, stream);
+		    fprintf_filtered (stream, "typedef ");
+
+		    /* We want to print typedefs with substitutions
+		       from the template parameters or globally-known
+		       typedefs but not local typedefs.  */
+		    c_print_type (target,
+				  TYPE_TYPEDEF_FIELD_NAME (type, i),
+				  stream, show - 1, level + 4,
+				  &semi_local_flags);
+		    fprintf_filtered (stream, ";\n");
+		  }
+	      }
 
-		  /* Dereference the typedef declaration itself.  */
-		  gdb_assert (TYPE_CODE (target) == TYPE_CODE_TYPEDEF);
-		  target = TYPE_TARGET_TYPE (target);
+	    fprintfi_filtered (level, stream, "}");
 
-		  print_spaces_filtered (level + 4, stream);
-		  fprintf_filtered (stream, "typedef ");
-		  c_print_type (target, TYPE_TYPEDEF_FIELD_NAME (type, i),
-				stream, show - 1, level + 4, flags);
-		  fprintf_filtered (stream, ";\n");
-		}
-	    }
-
-	  fprintfi_filtered (level, stream, "}");
+	    if (TYPE_LOCALTYPE_PTR (type) && show >= 0)
+	      fprintfi_filtered (level,
+				 stream, _(" (Local at %s:%d)\n"),
+				 TYPE_LOCALTYPE_FILE (type),
+				 TYPE_LOCALTYPE_LINE (type));
+	  }
 
-	  if (TYPE_LOCALTYPE_PTR (type) && show >= 0)
-	    fprintfi_filtered (level,
-			       stream, _(" (Local at %s:%d)\n"),
-			       TYPE_LOCALTYPE_FILE (type),
-			       TYPE_LOCALTYPE_LINE (type));
-	}
+	do_cleanups (local_cleanups);
+      }
       break;
 
     case TYPE_CODE_ENUM:
@@ -1177,7 +1332,7 @@ c_type_print_base (struct type *type, struct ui_file *stream,
       if (TYPE_TAG_NAME (type) != NULL
 	  && strncmp (TYPE_TAG_NAME (type), "{unnamed", 8))
 	{
-	  fputs_filtered (TYPE_TAG_NAME (type), stream);
+	  print_name_maybe_canonical (TYPE_TAG_NAME (type), flags, stream);
 	  if (show > 0)
 	    fputs_filtered (" ", stream);
 	}
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 21d9043..eff67d9 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -2406,7 +2406,7 @@ integer_types_same_name_p (const char *first, const char *second)
 /* Compares type A to type B returns 1 if the represent the same type
    0 otherwise.  */
 
-static int
+int
 types_equal (struct type *a, struct type *b)
 {
   /* Identical type pointers.  */
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 991026b..f16c43d 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1657,4 +1657,6 @@ extern struct type *copy_type_recursive (struct objfile *objfile,
 
 extern struct type *copy_type (const struct type *type);
 
+extern int types_equal (struct type *, struct type *);
+
 #endif /* GDBTYPES_H */
diff --git a/gdb/testsuite/gdb.cp/ptype-flags.cc b/gdb/testsuite/gdb.cp/ptype-flags.cc
new file mode 100644
index 0000000..3077f73
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/ptype-flags.cc
@@ -0,0 +1,47 @@
+/* Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+   */
+
+template<typename S>
+class Simple
+{
+  S val;
+};
+
+template<typename T>
+class Base
+{
+};
+
+template<typename T>
+class Holder : public Base<T>
+{
+public:
+  Simple<T> t;
+  Simple<T*> tstar;
+
+  typedef Simple< Simple<T> > Z;
+
+  Z z;
+
+  double method(void) { return 23.0; }
+};
+
+Holder<int> value;
+
+int main()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/ptype-flags.exp b/gdb/testsuite/gdb.cp/ptype-flags.exp
new file mode 100644
index 0000000..a992b9c
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/ptype-flags.exp
@@ -0,0 +1,85 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set nl		"\[\r\n\]+"
+
+if { [skip_cplus_tests] } { continue }
+
+load_lib "cp-support.exp"
+
+standard_testfile .cc
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+    return -1
+}
+
+if ![runto_main] then {
+    perror "couldn't run to breakpoint"
+    return
+}
+
+gdb_test_no_output "set language c++" ""
+gdb_test_no_output "set width 0" ""
+
+proc do_check {name {flags ""} {show_typedefs 1} {show_methods 1} {raw 0}} {
+    set contents {
+	{ base "public Base<T>" }
+	{ field public "Simple<T> t;" }
+	{ field public "Simple<T*> tstar;" }
+    }
+
+    if {$raw} {
+	lappend contents { field public "Holder<int>::Z z;" }
+    } else {
+	lappend contents { field public "Z z;" }
+    }
+
+    if {$show_typedefs} {
+	lappend contents { typedef public "Simple<Simple<T> > Z;" }
+    }
+
+    if {$show_methods} {
+	lappend contents { method public "double method();" }
+    }
+
+    if {$raw} {
+	regsub -all -- "T" $contents "int" contents
+    }
+
+    cp_test_ptype_class value $name "class" "Holder<int>" $contents \
+	"" {} $flags
+}
+
+do_check "basic test"
+do_check "no methods" "/m" 1 0
+do_check "no typedefs" "/t" 0 1
+do_check "no methods or typedefs" "/mt" 0 0
+
+do_check "raw" "/r" 1 1 1
+do_check "raw no methods" "/rm" 1 0 1
+do_check "raw no typedefs" "/rt" 0 1 1
+do_check "raw no methods or typedefs" "/rmt" 0 0 1
+
+gdb_test_no_output "set print type methods off"
+do_check "basic test, default methods off" "" 1 0
+do_check "methods, default methods off" "/M" 1 1
+do_check "no typedefs, default methods off" "/t" 0 0
+do_check "methods, no typedefs, default methods off" "/Mt" 0 1
+
+gdb_test_no_output "set print type typedefs off"
+do_check "basic test, default methods+typedefs off" "" 0 0
+do_check "methods, default methods+typedefs off" "/M" 0 1
+do_check "typedefs, default methods+typedefs off" "/T" 1 0
+do_check "methods typedefs, default methods+typedefs off" "/MT" 1 1
diff --git a/gdb/testsuite/gdb.cp/templates.exp b/gdb/testsuite/gdb.cp/templates.exp
index 47915b1..9ebb3bd 100644
--- a/gdb/testsuite/gdb.cp/templates.exp
+++ b/gdb/testsuite/gdb.cp/templates.exp
@@ -40,7 +40,7 @@ proc test_ptype_of_templates {} {
     global gdb_prompt
     global ws
 
-    gdb_test_multiple "ptype T5<int>" "ptype T5<int>" {
+    gdb_test_multiple "ptype/r T5<int>" "ptype T5<int>" {
 	-re "type = class T5<int> \{${ws}public:${ws}static int X;${ws}int x;${ws}int val;${ws}T5<int> & operator=\\(T5<int> const ?&\\);${ws}T5\\(int\\);${ws}T5\\((T5<int> const|const T5<int>) ?&\\);${ws}~T5\\((void|)\\);${ws}static void \\* operator new\\(unsigned( int| long)?\\);${ws}static void operator delete\\(void ?\\*\\);${ws}int value\\((void|)\\);${ws}\}\r\n$gdb_prompt $" {
 	    xfail "ptype T5<int> -- new without size_t"
 	}
@@ -63,7 +63,7 @@ proc test_ptype_of_templates {} {
 	}
     }
 
-    gdb_test_multiple "ptype t5i" "ptype t5i" {
+    gdb_test_multiple "ptype/r t5i" "ptype t5i" {
         -re "type = class T5<int> \\{${ws}public:${ws}static int X;${ws}int x;${ws}int val;\r\n${ws}T5\\(int\\);${ws}T5\\(T5<int> const ?&\\);${ws}~T5\\((void|)\\);${ws}static void \\* operator new\\(unsigned( int| long)?\\);${ws}static void operator delete\\(void ?\\*\\);${ws}int value\\((void|)\\);${ws}\\}\r\n$gdb_prompt $" {
 	    xfail "ptype T5<int> -- with several fixes from 4.17 -- without size_t"
 	}
@@ -226,11 +226,13 @@ proc test_template_typedef {} {
 proc test_template_args {} {
 
     set empty_re "Empty *<void *\\(FunctionArg *<int>\\)>"
-    gdb_test "ptype empty" \
-	"type = (struct|class) $empty_re {.*<no data fields>.*}"
+    gdb_test "ptype/r empty" \
+	"type = (struct|class) $empty_re {.*<no data fields>.*}" \
+	"ptype empty"
 
-    gdb_test "ptype arg" \
-	"type = (struct|class) FunctionArg<int> {.*int method\\($empty_re \\&\\);.*}"
+    gdb_test "ptype/r arg" \
+	"type = (struct|class) FunctionArg<int> {.*int method\\($empty_re \\&\\);.*}" \
+	"ptype arg"
 }
 
 proc do_tests {} {
@@ -291,7 +293,7 @@ gdb_test "print fvpchar" \
 # NOTE: carlton/2003-02-26: However, because of a bug in the way GDB
 # handles nested types, we don't get this right in the DWARF-2 case.
 
-gdb_test_multiple "ptype Foo" "ptype Foo" {
+gdb_test_multiple "ptype/r Foo" "ptype Foo" {
     -re "type = template <(class |)T> (class |)Foo \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*T t;\r\n\\}\r\ntemplate instantiations:\r\n\[ \t\]*(class |)Foo<volatile char \\*>\r\n\[ \t\]*(class |)Foo<char>\r\n\[ \t\]*(class |)Foo<int>\r\n$gdb_prompt $" {
 	pass "ptype Foo"
     }
@@ -312,7 +314,7 @@ gdb_test_multiple "ptype Foo" "ptype Foo" {
 
 # ptype Foo<int>
 
-gdb_test_multiple "ptype fint" "ptype fint" {
+gdb_test_multiple "ptype/r fint" "ptype fint" {
     -re "type = (class |)Foo<int> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*.*int foo\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype fint"
     }
@@ -323,7 +325,7 @@ gdb_test_multiple "ptype fint" "ptype fint" {
 
 # ptype Foo<char>
 
-gdb_test_multiple "ptype fchar" "ptype fchar" {
+gdb_test_multiple "ptype/r fchar" "ptype fchar" {
     -re "type = (class |)Foo<char> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*char t;\r\n\r\n\[ \t\]*.*char foo\\(int, char\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype fchar"
     }
@@ -334,7 +336,7 @@ gdb_test_multiple "ptype fchar" "ptype fchar" {
 
 # ptype Foo<volatile char *>
 
-gdb_test_multiple "ptype fvpchar" "ptype fvpchar" {
+gdb_test_multiple "ptype/r fvpchar" "ptype fvpchar" {
     -re "type = (class |)Foo<volatile char ?\\*> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*.*char.*\\*t;\r\n\r\n\[ \t\]*.*char \\* foo\\(int,.*char.*\\*\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype fvpchar"
     }
@@ -374,7 +376,7 @@ gdb_test_multiple "print Foo<volatile char*>::foo" "print Foo<volatile char*>::f
 # Template Bar<T, int>
 
 # same as Foo for g++
-gdb_test_multiple "ptype Bar" "ptype Bar" {
+gdb_test_multiple "ptype/r Bar" "ptype Bar" {
     -re "type = template <(class |)T, (class |)sz> (class |)Bar \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*T t;\r\n\\}\r\ntemplate instantiations:\r\n\[ \t\]*(class |)Bar<int,(\\(int\\)|)1>\r\n\[ \t\]*(class |)Bar<int,(\\(int\\)|)33>\r\n$gdb_prompt $" {
 	pass "ptype Bar"
     }
@@ -394,7 +396,7 @@ gdb_test_multiple "ptype Bar" "ptype Bar" {
 
 # ptype Bar<int,33>
 
-gdb_test_multiple "ptype bint" "ptype bint" {
+gdb_test_multiple "ptype/r bint" "ptype bint" {
     -re "type = (class |)Bar<int, ?(\\(int\\)|)33> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*.*int bar\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype bint"
     }
@@ -405,7 +407,7 @@ gdb_test_multiple "ptype bint" "ptype bint" {
 
 # ptype Bar<int, (4>3)>
 
-gdb_test_multiple "ptype bint2" "ptype bint2" {
+gdb_test_multiple "ptype/r bint2" "ptype bint2" {
     -re "type = (class |)Bar<int, ?(\\(int\\)|)1> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*.*int bar\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype bint2"
     }
@@ -417,7 +419,7 @@ gdb_test_multiple "ptype bint2" "ptype bint2" {
 # Template Baz<T, char>
 
 # Same as Foo, for g++
-gdb_test_multiple "ptype Baz" "ptype Baz" {
+gdb_test_multiple "ptype/r Baz" "ptype Baz" {
     -re "type = template <(class |)T, ?(class |)sz> (class |)Baz \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*T t;\r\n\\}\r\ntemplate instantiations:\r\n\[ \t\]*(class |)Baz<char,(\\(char\\)|)97>\r\n\[ \t\]*(class |)Baz<int,(\\(char\\)|)115>\r\n$gdb_prompt $" {
 	pass "ptype Baz"
     }
@@ -441,7 +443,7 @@ gdb_test_multiple "ptype Baz" "ptype Baz" {
 
 # ptype Baz<int, 's'>
 
-gdb_test_multiple "ptype bazint" "ptype bazint" {
+gdb_test_multiple "ptype/r bazint" "ptype bazint" {
     -re "type = (class |)Baz<int, ?(\\(char\\)|)(115|\\'s\\')> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*.*int baz\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype bazint"
     }
@@ -452,7 +454,7 @@ gdb_test_multiple "ptype bazint" "ptype bazint" {
 
 # ptype Baz<char, 'a'>
 
-gdb_test_multiple "ptype bazint2" "ptype bazint2" {
+gdb_test_multiple "ptype/r bazint2" "ptype bazint2" {
     -re "type = (class |)Baz<char, ?(\\(char\\)|)(97|\\'a\\')> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*char t;\r\n\r\n\[ \t\]*.*char baz\\(int, char\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype bazint2"
     }
@@ -463,7 +465,7 @@ gdb_test_multiple "ptype bazint2" "ptype bazint2" {
 
 # Template Qux<T, int (*f)(int) >
 # Same as Foo for g++
-gdb_test_multiple "ptype Qux" "ptype Qux" {
+gdb_test_multiple "ptype/r Qux" "ptype Qux" {
     -re "type = template <(class |)T, ?(class |)sz> (class |)Qux \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*T t;\r\n\\}\r\ntemplate instantiations:\r\n\[ \t\]*(class |)Qux<int,&string>\r\n\[ \t\]*(class |)Qux<char,&string>\r\n$gdb_prompt $" {
 	pass "ptype Qux"
     }
@@ -486,7 +488,7 @@ gdb_test_multiple "ptype Qux" "ptype Qux" {
 
 # pt Qux<int,&string>
 
-gdb_test_multiple "ptype quxint" "ptype quxint" {
+gdb_test_multiple "ptype/r quxint" "ptype quxint" {
     -re "type = class Qux<int, ?& ?string> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*.*int qux\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype quxint"
     }
@@ -505,7 +507,7 @@ gdb_test_multiple "ptype quxint" "ptype quxint" {
 # Template Spec<T1, T2>
 
 # Same as Foo for g++
-gdb_test_multiple "ptype Spec" "ptype Spec" {
+gdb_test_multiple "ptype/r Spec" "ptype Spec" {
     -re "type = template <(class |)T1, (class |)T2> (class |)Spec \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\\}\r\ntemplate instantiations:\r\n\[ \t\]*(class |)Spec<int,int \\*>\r\n\[ \t\]*(class |)Spec<int,char>\r\n$gdb_prompt $" {
 	pass "ptype Spec"
     }
@@ -524,7 +526,7 @@ gdb_test_multiple "ptype Spec" "ptype Spec" {
 
 # pt Spec<char,0>
 
-gdb_test_multiple "ptype siip" "ptype siip" {
+gdb_test_multiple "ptype/r siip" "ptype siip" {
     -re "type = class Spec<int, ?int ?\\*> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\r\n\[ \t\]*.*int spec\\(int ?\\*\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype siip"
     }
@@ -535,7 +537,7 @@ gdb_test_multiple "ptype siip" "ptype siip" {
 
 # pt Garply<int>
 
-gdb_test_multiple "ptype Garply<int>" "ptype Garply<int>" {
+gdb_test_multiple "ptype/r Garply<int>" "ptype Garply<int>" {
     -re "type = class Garply<int> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*.*int garply\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype Garply<int>"
     }
@@ -546,7 +548,7 @@ gdb_test_multiple "ptype Garply<int>" "ptype Garply<int>" {
 
 # ptype of nested template name
 
-gdb_test_multiple "ptype Garply<Garply<char> >" "ptype Garply<Garply<char> >" {
+gdb_test_multiple "ptype/r Garply<Garply<char> >" "ptype Garply<Garply<char> >" {
     -re "type = (class |)Garply<Garply<char> > \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*.*(class |)Garply<char> t;\r\n\r\n\[ \t\]*.*(class |)Garply<char> garply\\(int, (class |)Garply<char>\\);\r\n\\}\r\n$gdb_prompt $" {
 	pass "ptype Garply<Garply<char> >"
     }
diff --git a/gdb/testsuite/lib/cp-support.exp b/gdb/testsuite/lib/cp-support.exp
index b984f4d..827fda8 100644
--- a/gdb/testsuite/lib/cp-support.exp
+++ b/gdb/testsuite/lib/cp-support.exp
@@ -101,6 +101,8 @@ proc cp_check_errata { expected_string actual_string errata_table } {
 # demangler syntax adjustment, so you have to make a bigger table
 # with lines for each output variation.
 # 
+# IN_PTYPE_ARG are arguments to pass to ptype.  The default is "/r".
+#
 # gdb can vary the output of ptype in several ways:
 #
 # . CLASS/STRUCT
@@ -179,15 +181,16 @@ proc cp_check_errata { expected_string actual_string errata_table } {
 #
 # -- chastain 2004-08-07
 
-proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_tail "" } { in_errata_table { } } } {
+proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_tail "" } { in_errata_table { } } { in_ptype_arg /r } } {
     global gdb_prompt
     set wsopt "\[\r\n\t \]*"
 
-    # The test name defaults to the command.
+    # The test name defaults to the command, but without the
+    # arguments, for historical reasons.
 
     if { "$in_testname" == "" } then { set in_testname "ptype $in_exp" }
 
-    set in_command "ptype $in_exp"
+    set in_command "ptype${in_ptype_arg} $in_exp"
 
     # Save class tables in a history array for reuse.
 
@@ -233,13 +236,13 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 
     set parse_okay 0
     gdb_test_multiple "$in_command" "$in_testname // parse failed" {
-	-re "type = (struct|class)${wsopt}(\[A-Za-z0-9_\]*)${wsopt}((:\[^\{\]*)?)${wsopt}\{(.*)\}${wsopt}(\[^\r\n\]*)\[\r\n\]+$gdb_prompt $" {
+	-re "type = (struct|class)${wsopt}(\[^ \t\]*)${wsopt}(\\\[with .*\\\]${wsopt})?((:\[^\{\]*)?)${wsopt}\{(.*)\}${wsopt}(\[^\r\n\]*)\[\r\n\]+$gdb_prompt $" {
 	    set parse_okay          1
 	    set actual_key          $expect_out(1,string)
 	    set actual_tag          $expect_out(2,string)
-	    set actual_base_string  $expect_out(3,string)
-	    set actual_body         $expect_out(5,string)
-	    set actual_tail         $expect_out(6,string)
+	    set actual_base_string  $expect_out(4,string)
+	    set actual_body         $expect_out(6,string)
+	    set actual_tail         $expect_out(7,string)
 	}
     }
     if { ! $parse_okay } then { return }
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 509b3ee..0e1c93c 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -51,7 +51,8 @@ const struct type_print_options type_print_raw_options =
 {
   1,				/* raw */
   1,				/* print_methods */
-  1				/* print_typedefs */
+  1,				/* print_typedefs */
+  NULL				/* local_typedefs */
 };
 
 /* The default flags for 'ptype' and 'whatis'.  */
@@ -60,11 +61,202 @@ static struct type_print_options default_ptype_flags =
 {
   0,				/* raw */
   1,				/* print_methods */
-  1				/* print_typedefs */
+  1,				/* print_typedefs */
+  NULL				/* local_typedefs */
 };
 
 
 
+/* A hash table holding typedef_field objects.  This is more
+   complicated than an ordinary hash because it must also track the
+   lifetime of some -- but not all -- of the contained objects.  */
+
+struct typedef_hash_table
+{
+  /* The actual hash table.  */
+  htab_t table;
+
+  /* Storage for typedef_field objects that must be synthesized.  */
+  struct obstack storage;
+};
+
+/* A hash function for a typedef_field.  */
+
+static hashval_t
+hash_typedef_field (const void *p)
+{
+  const struct typedef_field *tf = p;
+  struct type *t = check_typedef (tf->type);
+
+  return htab_hash_string (TYPE_SAFE_NAME (t));
+}
+
+/* An equality function for a typedef field.  */
+
+static int
+eq_typedef_field (const void *a, const void *b)
+{
+  const struct typedef_field *tfa = a;
+  const struct typedef_field *tfb = b;
+
+  return types_equal (tfa->type, tfb->type);
+}
+
+/* Add typedefs from T to the hash table TABLE.  */
+
+void
+recursively_update_typedef_hash (struct typedef_hash_table *table,
+				 struct type *t)
+{
+  int i;
+
+  if (table == NULL)
+    return;
+
+  for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (t); ++i)
+    {
+      struct typedef_field *tdef = &TYPE_TYPEDEF_FIELD (t, i);
+      void **slot;
+
+      slot = htab_find_slot (table->table, tdef, INSERT);
+      /* Only add a given typedef name once.  Really this shouldn't
+	 happen; but it is safe enough to do the updates breadth-first
+	 and thus use the most specific typedef.  */
+      if (*slot == NULL)
+	*slot = tdef;
+    }
+
+  /* Recurse into superclasses.  */
+  for (i = 0; i < TYPE_N_BASECLASSES (t); ++i)
+    recursively_update_typedef_hash (table, TYPE_BASECLASS (t, i));
+}
+
+/* Add template parameters from T to the typedef hash TABLE.  */
+
+void
+add_template_parameters (struct typedef_hash_table *table, struct type *t)
+{
+  int i;
+
+  if (table == NULL)
+    return;
+
+  for (i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (t); ++i)
+    {
+      struct typedef_field *tf;
+      void **slot;
+
+      /* We only want type-valued template parameters in the hash.  */
+      if (SYMBOL_CLASS (TYPE_TEMPLATE_ARGUMENT (t, i)) != LOC_TYPEDEF)
+	continue;
+
+      tf = XOBNEW (&table->storage, struct typedef_field);
+      tf->name = SYMBOL_LINKAGE_NAME (TYPE_TEMPLATE_ARGUMENT (t, i));
+      tf->type = SYMBOL_TYPE (TYPE_TEMPLATE_ARGUMENT (t, i));
+
+      slot = htab_find_slot (table->table, tf, INSERT);
+      if (*slot == NULL)
+	*slot = tf;
+    }
+}
+
+/* Create a new typedef-lookup hash table.  */
+
+struct typedef_hash_table *
+create_typedef_hash (void)
+{
+  struct typedef_hash_table *result;
+
+  result = XNEW (struct typedef_hash_table);
+  result->table = htab_create_alloc (10, hash_typedef_field, eq_typedef_field,
+				     NULL, xcalloc, xfree);
+  obstack_init (&result->storage);
+
+  return result;
+}
+
+/* Free a typedef field table.  */
+
+void
+free_typedef_hash (struct typedef_hash_table *table)
+{
+  if (table != NULL)
+    {
+      htab_delete (table->table);
+      obstack_free (&table->storage, NULL);
+      xfree (table);
+    }
+}
+
+/* A cleanup for freeing a typedef_hash_table.  */
+
+static void
+do_free_typedef_hash (void *arg)
+{
+  free_typedef_hash (arg);
+}
+
+/* Return a new cleanup that frees TABLE.  */
+
+struct cleanup *
+make_cleanup_free_typedef_hash (struct typedef_hash_table *table)
+{
+  return make_cleanup (do_free_typedef_hash, table);
+}
+
+/* Helper function for copy_typedef_hash.  */
+
+static int
+copy_typedef_hash_element (void **slot, void *nt)
+{
+  htab_t new_table = nt;
+  void **new_slot;
+
+  new_slot = htab_find_slot (new_table, *slot, INSERT);
+  if (*new_slot == NULL)
+    *new_slot = *slot;
+
+  return 1;
+}
+
+/* Copy a typedef hash.  */
+
+struct typedef_hash_table *
+copy_typedef_hash (struct typedef_hash_table *table)
+{
+  struct typedef_hash_table *result;
+
+  if (table == NULL)
+    return NULL;
+
+  result = create_typedef_hash ();
+  htab_traverse_noresize (table->table, copy_typedef_hash_element,
+			  result->table);
+  return result;
+}
+
+/* Look up the type T in the typedef hash table in with FLAGS.  If T
+   is in the table, return its short (class-relative) typedef name.
+   Otherwise return NULL.  If the table is NULL, this always returns
+   NULL.  */
+
+const char *
+find_typedef_in_hash (const struct type_print_options *flags, struct type *t)
+{
+  struct typedef_field tf, *found;
+
+  if (flags->local_typedefs == NULL)
+    return NULL;
+
+  tf.name = NULL;
+  tf.type = t;
+  found = htab_find (flags->local_typedefs->table, &tf);
+
+  return found == NULL ? NULL : found->name;
+}
+
+
+
 /* Print a description of a type in the format of a 
    typedef for the current language.
    NEW is the new name for a type TYPE.  */
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index 1e15097..71bac01 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -22,6 +22,7 @@
 
 enum language;
 struct ui_file;
+struct typedef_hash_table;
 
 struct type_print_options
 {
@@ -33,10 +34,30 @@ struct type_print_options
 
   /* True means print typedefs in a class.  */
   unsigned int print_typedefs : 1;
+
+  /* If not NULL, a local typedef hash table used when printing a
+     type.  */
+  struct typedef_hash_table *local_typedefs;
 };
 
 extern const struct type_print_options type_print_raw_options;
 
+void recursively_update_typedef_hash (struct typedef_hash_table *,
+				      struct type *);
+
+void add_template_parameters (struct typedef_hash_table *, struct type *);
+
+struct typedef_hash_table *create_typedef_hash (void);
+
+void free_typedef_hash (struct typedef_hash_table *);
+
+struct cleanup *make_cleanup_free_typedef_hash (struct typedef_hash_table *);
+
+struct typedef_hash_table *copy_typedef_hash (struct typedef_hash_table *);
+
+const char *find_typedef_in_hash (const struct type_print_options *,
+				  struct type *);
+
 void print_type_scalar (struct type * type, LONGEST, struct ui_file *);
 
 void c_type_print_varspec_suffix (struct type *, struct ui_file *, int,
-- 
1.7.7.6


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