diff --git a/gdb/NEWS b/gdb/NEWS
index fbf5591781..076b02ba9b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -94,6 +94,10 @@ maint info selftests
 starti
   Start the debugged program stopping at the first instruction.
 
+set|show print type nested-type-limit
+  Set and show the number of recursively defined nested types that the
+  type printer will show.
+
 * TUI Single-Key mode now supports two new shortcut keys: `i' for stepi and
   `o' for nexti.
 
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 22fdaa5beb..983b7308a3 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -1334,11 +1334,31 @@ c_type_print_base (struct type *type, struct ui_file *stream,
 		}
 	    }
 
+	  /* Print out nested types.  */
+	  if (TYPE_NESTED_TYPES_COUNT (type) != 0
+	      && semi_local_flags.print_nested_type_limit != 0)
+	    {
+	      if (semi_local_flags.print_nested_type_limit > 0)
+		--semi_local_flags.print_nested_type_limit;
+
+	      if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0)
+		fprintf_filtered (stream, "\n");
+
+	      for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i)
+		{
+		  print_spaces_filtered (level + 4, stream);
+		  c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i),
+				"", stream, show, level + 4, &semi_local_flags);
+		  fprintf_filtered (stream, ";\n");
+		}
+	    }
+
 	  /* Print typedefs defined in this class.  */
 
 	  if (TYPE_TYPEDEF_FIELD_COUNT (type) != 0 && flags->print_typedefs)
 	    {
-	      if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0)
+	      if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0
+		  || TYPE_NESTED_TYPES_COUNT (type) != 0)
 		fprintf_filtered (stream, "\n");
 
 	      for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index bfeb7a9a35..9729caa5cd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -16942,6 +16942,17 @@ cause @value{GDBN} to omit the methods.
 This command shows the current setting of method display when printing
 classes.
 
+@kindex set print type nested-type-limit
+@item set print type nested-type-limit @var{limit}
+Set the recursive display limit of nested types that the type printer will
+show.  A @var{limit} of -1 is treated as unlimited.  By default, the type
+printer will not show any nested types defined in classes.
+
+@kindex show print type nested-type-limit
+@item show print type nested-type-limit
+This command shows the current display limit of nested types when
+printing classes.
+
 @kindex set print type typedefs
 @item set print type typedefs
 @itemx set print type typedefs on
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 686fa3f3d3..984f53ce2c 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -1461,10 +1461,10 @@ struct fnfieldlist
   struct nextfnfield *head;
 };
 
-struct typedef_field_list
+struct decl_field_list
 {
-  struct typedef_field field;
-  struct typedef_field_list *next;
+  struct decl_field field;
+  struct decl_field_list *next;
 };
 
 /* The routines that read and process dies for a C struct or C++ class
@@ -1494,8 +1494,13 @@ struct field_info
 
     /* typedefs defined inside this class.  TYPEDEF_FIELD_LIST contains head of
        a NULL terminated list of TYPEDEF_FIELD_LIST_COUNT elements.  */
-    struct typedef_field_list *typedef_field_list;
+    struct decl_field_list *typedef_field_list;
     unsigned typedef_field_list_count;
+
+    /* Nested types defined by this class and the number of elements in this
+       list.  */
+    struct decl_field_list *nested_types_list;
+    unsigned nested_types_list_count;
   };
 
 /* One item on the queue of compilation units to read in full symbols
@@ -7139,7 +7144,8 @@ add_partial_symbol (struct partial_die_info *pdi, struct dwarf2_cu *cu)
     {
     case DW_TAG_subprogram:
       addr = gdbarch_adjust_dwarf2_addr (gdbarch, pdi->lowpc + baseaddr);
-      if (pdi->is_external || cu->language == language_ada)
+      if (pdi->is_external || cu->language == language_ada
+	  || cu->language == language_cplus)
 	{
           /* brobecker/2007-12-26: Normally, only "external" DIEs are part
              of the global scope.  But in Ada, we want to be able to access
@@ -7392,7 +7398,7 @@ add_partial_subprogram (struct partial_die_info *pdi,
   if (! pdi->has_children)
     return;
 
-  if (cu->language == language_ada)
+  if (cu->language == language_ada || cu->language == language_cplus)
     {
       pdi = pdi->die_child;
       while (pdi != NULL)
@@ -12635,7 +12641,7 @@ dwarf2_get_subprogram_pc_bounds (struct die_info *die,
 
   /* If the language does not allow nested subprograms (either inside
      subprograms or lexical blocks), we're done.  */
-  if (cu->language != language_ada)
+  if (cu->language != language_ada && cu->language != language_cplus)
     return;
 
   /* Check all the children of the given DIE.  If it contains nested
@@ -13088,28 +13094,29 @@ dwarf2_add_field (struct field_info *fip, struct die_info *die,
     }
 }
 
-/* Add a typedef defined in the scope of the FIP's class.  */
+/* Add a type definition defined in the scope of the FIP's class.  */
 
 static void
-dwarf2_add_typedef (struct field_info *fip, struct die_info *die,
-		    struct dwarf2_cu *cu)
+dwarf2_add_type_defn (struct field_info *fip, struct die_info *die,
+		      struct dwarf2_cu *cu)
 {
-  struct typedef_field_list *new_field;
-  struct typedef_field *fp;
+  struct decl_field_list *new_field;
+  struct decl_field *fp;
 
   /* Allocate a new field list entry and link it in.  */
-  new_field = XCNEW (struct typedef_field_list);
+  new_field = XCNEW (struct decl_field_list);
   make_cleanup (xfree, new_field);
 
-  gdb_assert (die->tag == DW_TAG_typedef);
+  gdb_assert (die->tag == DW_TAG_typedef
+	      || die->tag == DW_TAG_class_type
+	      || die->tag == DW_TAG_structure_type
+	      || die->tag == DW_TAG_union_type
+	      || die->tag == DW_TAG_enumeration_type);
 
   fp = &new_field->field;
 
-  /* Get name of field.  */
+  /* Get name of field.  NULL is okay here, meaning an anonymous type.  */
   fp->name = dwarf2_name (die, cu);
-  if (fp->name == NULL)
-    return;
-
   fp->type = read_type_die (die, cu);
 
   /* Save accessibility.  */
@@ -13135,9 +13142,18 @@ dwarf2_add_typedef (struct field_info *fip, struct die_info *die,
 		 _("Unhandled DW_AT_accessibility value (%x)"), accessibility);
     }
 
-  new_field->next = fip->typedef_field_list;
-  fip->typedef_field_list = new_field;
-  fip->typedef_field_list_count++;
+  if (die->tag == DW_TAG_typedef)
+    {
+      new_field->next = fip->typedef_field_list;
+      fip->typedef_field_list = new_field;
+      fip->typedef_field_list_count++;
+    }
+  else
+    {
+      new_field->next = fip->nested_types_list;
+      fip->nested_types_list = new_field;
+      fip->nested_types_list_count++;
+    }
 }
 
 /* Create the vector of fields, and attach it to the type.  */
@@ -13761,8 +13777,12 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
 	      /* C++ base class field.  */
 	      dwarf2_add_field (&fi, child_die, cu);
 	    }
-	  else if (child_die->tag == DW_TAG_typedef)
-	    dwarf2_add_typedef (&fi, child_die, cu);
+	  else if (child_die->tag == DW_TAG_typedef
+		   || child_die->tag == DW_TAG_class_type
+		   || child_die->tag == DW_TAG_structure_type
+		   || child_die->tag == DW_TAG_union_type
+		   || child_die->tag == DW_TAG_enumeration_type)
+	    dwarf2_add_type_defn (&fi, child_die, cu);
 	  else if (child_die->tag == DW_TAG_template_type_param
 		   || child_die->tag == DW_TAG_template_value_param)
 	    {
@@ -13871,14 +13891,14 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
 
 	  ALLOCATE_CPLUS_STRUCT_TYPE (type);
 	  TYPE_TYPEDEF_FIELD_ARRAY (type)
-	    = ((struct typedef_field *)
+	    = ((struct decl_field *)
 	       TYPE_ALLOC (type, sizeof (TYPE_TYPEDEF_FIELD (type, 0)) * i));
 	  TYPE_TYPEDEF_FIELD_COUNT (type) = i;
 
 	  /* Reverse the list order to keep the debug info elements order.  */
 	  while (--i >= 0)
 	    {
-	      struct typedef_field *dest, *src;
+	      struct decl_field *dest, *src;
 
 	      dest = &TYPE_TYPEDEF_FIELD (type, i);
 	      src = &fi.typedef_field_list->field;
@@ -13887,6 +13907,30 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
 	    }
 	}
 
+      /* Copy fi.nested_types_list linked list elements content into the
+	 allocated array TYPE_NESTED_TYPES_ARRAY (type).  */
+      if (fi.nested_types_list != NULL)
+	{
+	  int i = fi.nested_types_list_count;
+
+	  ALLOCATE_CPLUS_STRUCT_TYPE (type);
+	  TYPE_NESTED_TYPES_ARRAY (type)
+	    = ((struct decl_field *)
+	       TYPE_ALLOC (type, sizeof (struct decl_field) * i));
+	  TYPE_NESTED_TYPES_COUNT (type) = i;
+
+	  /* Reverse the list order to keep the debug info elements order.  */
+	  while (--i >= 0)
+	    {
+	      struct decl_field *dest, *src;
+
+	      dest = &TYPE_NESTED_TYPES_FIELD (type, i);
+	      src = &fi.nested_types_list->field;
+	      fi.nested_types_list = fi.nested_types_list->next;
+	      *dest = *src;
+	    }
+	}
+
       do_cleanups (back_to);
     }
 
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 5c1aecd211..d01c59a88f 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -863,7 +863,7 @@ struct fn_field
 
 };
 
-struct typedef_field
+struct decl_field
 {
   /* * Unqualified name to be prefixed by owning class qualified
      name.  */
@@ -978,10 +978,17 @@ struct cplus_struct_type
     /* * typedefs defined inside this class.  typedef_field points to
        an array of typedef_field_count elements.  */
 
-    struct typedef_field *typedef_field;
+    struct decl_field *typedef_field;
 
     unsigned typedef_field_count;
 
+    /* * The nested types defined by this type.  nested_types points to
+       an array of nested_types_count elements.  */
+
+    struct decl_field *nested_types;
+
+    unsigned nested_types_count;
+
     /* * The template arguments.  This is an array with
        N_TEMPLATE_ARGUMENTS elements.  This is NULL for non-template
        classes.  */
@@ -1424,6 +1431,23 @@ extern void set_type_vptr_basetype (struct type *, struct type *);
 #define TYPE_TYPEDEF_FIELD_PRIVATE(thistype, n)        \
   TYPE_TYPEDEF_FIELD (thistype, n).is_private
 
+#define TYPE_NESTED_TYPES_ARRAY(thistype)	\
+  TYPE_CPLUS_SPECIFIC (thistype)->nested_types
+#define TYPE_NESTED_TYPES_FIELD(thistype, n) \
+  TYPE_CPLUS_SPECIFIC (thistype)->nested_types[n]
+#define TYPE_NESTED_TYPES_FIELD_NAME(thistype, n) \
+  TYPE_NESTED_TYPES_FIELD (thistype, n).name
+#define TYPE_NESTED_TYPES_FIELD_TYPE(thistype, n) \
+  TYPE_NESTED_TYPES_FIELD (thistype, n).type
+#define TYPE_NESTED_TYPES_COUNT(thistype) \
+  TYPE_CPLUS_SPECIFIC (thistype)->nested_types_count
+#define TYPE_NESTED_TYPES_FIELD_PUBLIC(thistype, n)	\
+  TYPE_NESTED_TYPES_FIELD (thistype, n).is_public
+#define TYPE_NESTED_TYPES_FIELD_PROTECTED(thistype, n) \
+  TYPE_NESTED_TYPES_FIELD (thistype, n).is_protected
+#define TYPE_NESTED_TYPES_FIELD_PRIVATE(thistype, n)	\
+  TYPE_NESTED_TYPES_FIELD (thistype, n).is_private
+
 #define TYPE_IS_OPAQUE(thistype) \
   (((TYPE_CODE (thistype) == TYPE_CODE_STRUCT) \
     || (TYPE_CODE (thistype) == TYPE_CODE_UNION)) \
diff --git a/gdb/testsuite/gdb.cp/nested-types.cc b/gdb/testsuite/gdb.cp/nested-types.cc
new file mode 100644
index 0000000000..10ef6485c2
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nested-types.cc
@@ -0,0 +1,668 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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/>.  */
+
+/* Tests for nested type definitions.  */
+
+struct S10
+{
+  enum E10 { A10, B10, C10 };
+  union U10
+  {
+    int a;
+    char c;
+  };
+  int i10;
+  E10 e10;
+  U10 u10;
+
+  struct S11
+  {
+    enum E11 { A11, B11, C11 };
+    union U11
+    {
+      int a;
+      char c;
+    };
+    int i11;
+    E11 e11;
+    U11 u11;
+
+    struct S12
+    {
+      enum E12 { A12, B12, C12 };
+      union U12
+      {
+	int a;
+	char c;
+      };
+      int i12;
+      E12 e12;
+      U12 u12;
+
+      struct S13
+      {
+	enum E13 { A13, B13, C13 };
+	union U13
+	{
+	  int a;
+	  char c;
+	};
+	int i13;
+	E13 e13;
+	U13 u13;
+
+	struct S14
+	{
+	  enum E14 { A14, B14, C14 };
+	  union U14
+	  {
+	    int a;
+	    char c;
+	  };
+	  int i14;
+	  E14 e14;
+	  U14 u14;
+
+	  struct S15
+	  {
+	    enum E15 { A15, B15, C15 };
+	    union U15
+	    {
+	      int a;
+	      char c;
+	    };
+	    int i15;
+	    E15 e15;
+	    U15 u15;
+
+	    struct S16
+	    {
+	      enum E16 { A16, B16, C16 };
+	      union U16
+	      {
+		int a;
+		char c;
+	      };
+	      int i16;
+	      E16 e16;
+	      U16 u16;
+
+	      struct S17
+	      {
+		enum E17 { A17, B17, C17 };
+		union U17
+		{
+		  int a;
+		  char c;
+		};
+		int i17;
+		E17 e17;
+		U17 u17;
+
+		struct S18
+		{
+		  enum E18 { A18, B18, C18 };
+		  union U18
+		  {
+		    int a;
+		    char c;
+		  };
+		  int i18;
+		  E18 e18;
+		  U18 u18;
+
+		  struct S19
+		  {
+		    enum E19 { A19, B19, C19 };
+		    union U19
+		    {
+		      int a;
+		      char c;
+		    };
+		    int i19;
+		    E19 e19;
+		    U19 u19;
+		  };
+		};
+	      };
+	    };
+	  };
+	};
+      };
+    };
+  };
+
+  struct S21
+  {
+    enum E21 { A21, B21, C21 };
+    union U21
+    {
+      int a;
+      char c;
+    };
+    int i21;
+    E21 e21;
+    U21 u21;
+
+    struct S22
+    {
+      enum E22 { A22, B22, C22 };
+      union U22
+      {
+	int a;
+	char c;
+      };
+      int i22;
+      E22 e22;
+      U22 u22;
+
+      struct S23
+      {
+	enum E23 { A23, B23, C23 };
+	union U23
+	{
+	  int a;
+	  char c;
+	};
+	int i23;
+	E23 e23;
+	U23 u23;
+
+	struct S24
+	{
+	  enum E24 { A24, B24, C24 };
+	  union U24
+	  {
+	    int a;
+	    char c;
+	  };
+	  int i24;
+	  E24 e24;
+	  U24 u24;
+
+	  struct S25
+	  {
+	    enum E25 { A25, B25, C25 };
+	    union U25
+	    {
+	      int a;
+	      char c;
+	    };
+	    int i25;
+	    E25 e25;
+	    U25 u25;
+
+	    struct S26
+	    {
+	      enum E26 { A26, B26, C26 };
+	      union U26
+	      {
+		int a;
+		char c;
+	      };
+	      int i26;
+	      E26 e26;
+	      U26 u26;
+
+	      struct S27
+	      {
+		enum E27 { A27, B27, C27 };
+		union U27
+		{
+		  int a;
+		  char c;
+		};
+		int i27;
+		E27 e27;
+		U27 u27;
+
+		struct S28
+		{
+		  enum E28 { A28, B28, C28 };
+		  union U28
+		  {
+		    int a;
+		    char c;
+		  };
+		  int i28;
+		  E28 e28;
+		  U28 u28;
+
+		  struct S29
+		  {
+		    enum E29 { A29, B29, C29 };
+		    union U29
+		    {
+		      int a;
+		      char c;
+		    };
+		    int i29;
+		    E29 e29;
+		    U29 u29;
+		  };
+		};
+	      };
+	    };
+	  };
+	};
+      };
+    };
+  };
+
+  struct S31
+  {
+    enum E31 { A31, B31, C31 };
+    union U31
+    {
+      int a;
+      char c;
+    };
+    int i31;
+    E31 e31;
+    U31 u31;
+
+    struct S32
+    {
+      enum E32 { A32, B32, C32 };
+      union U32
+      {
+	int a;
+	char c;
+      };
+      int i32;
+      E32 e32;
+      U32 u32;
+
+      struct S33
+      {
+	enum E33 { A33, B33, C33 };
+	union U33
+	{
+	  int a;
+	  char c;
+	};
+	int i33;
+	E33 e33;
+	U33 u33;
+
+	struct S34
+	{
+	  enum E34 { A34, B34, C34 };
+	  union U34
+	  {
+	    int a;
+	    char c;
+	  };
+	  int i34;
+	  E34 e34;
+	  U34 u34;
+
+	  struct S35
+	  {
+	    enum E35 { A35, B35, C35 };
+	    union U35
+	    {
+	      int a;
+	      char c;
+	    };
+	    int i35;
+	    E35 e35;
+	    U35 u35;
+
+	    struct S36
+	    {
+	      enum E36 { A36, B36, C36 };
+	      union U36
+	      {
+		int a;
+		char c;
+	      };
+	      int i36;
+	      E36 e36;
+	      U36 u36;
+
+	      struct S37
+	      {
+		enum E37 { A37, B37, C37 };
+		union U37
+		{
+		  int a;
+		  char c;
+		};
+		int i37;
+		E37 e37;
+		U37 u37;
+
+		struct S38
+		{
+		  enum E38 { A38, B38, C38 };
+		  union U38
+		  {
+		    int a;
+		    char c;
+		  };
+		  int i38;
+		  E38 e38;
+		  U38 u38;
+
+		  struct S39
+		  {
+		    enum E39 { A39, B39, C39 };
+		    union U39
+		    {
+		      int a;
+		      char c;
+		    };
+		    int i39;
+		    E39 e39;
+		    U39 u39;
+		  };
+		};
+	      };
+	    };
+	  };
+	};
+      };
+    };
+  };
+
+  struct S41
+  {
+    enum E41 { A41, B41, C41 };
+    union U41
+    {
+      int a;
+      char c;
+    };
+    int i41;
+    E41 e41;
+    U41 u41;
+
+    struct S42
+    {
+      enum E42 { A42, B42, C42 };
+      union U42
+      {
+	int a;
+	char c;
+      };
+      int i42;
+      E42 e42;
+      U42 u42;
+
+      struct S43
+      {
+	enum E43 { A43, B43, C43 };
+	union U43
+	{
+	  int a;
+	  char c;
+	};
+	int i43;
+	E43 e43;
+	U43 u43;
+
+	struct S44
+	{
+	  enum E44 { A44, B44, C44 };
+	  union U44
+	  {
+	    int a;
+	    char c;
+	  };
+	  int i44;
+	  E44 e44;
+	  U44 u44;
+
+	  struct S45
+	  {
+	    enum E45 { A45, B45, C45 };
+	    union U45
+	    {
+	      int a;
+	      char c;
+	    };
+	    int i45;
+	    E45 e45;
+	    U45 u45;
+
+	    struct S46
+	    {
+	      enum E46 { A46, B46, C46 };
+	      union U46
+	      {
+		int a;
+		char c;
+	      };
+	      int i46;
+	      E46 e46;
+	      U46 u46;
+
+	      struct S47
+	      {
+		enum E47 { A47, B47, C47 };
+		union U47
+		{
+		  int a;
+		  char c;
+		};
+		int i47;
+		E47 e47;
+		U47 u47;
+
+		struct S48
+		{
+		  enum E48 { A48, B48, C48 };
+		  union U48
+		  {
+		    int a;
+		    char c;
+		  };
+		  int i48;
+		  E48 e48;
+		  U48 u48;
+
+		  struct S49
+		  {
+		    enum E49 { A49, B49, C49 };
+		    union U49
+		    {
+		      int a;
+		      char c;
+		    };
+		    int i49;
+		    E49 e49;
+		    U49 u49;
+		  };
+		};
+	      };
+	    };
+	  };
+	};
+      };
+    };
+  };
+
+  struct S51
+  {
+    enum E51 { A51, B51, C51 };
+    union U51
+    {
+      int a;
+      char c;
+    };
+    int i51;
+    E51 e51;
+    U51 u51;
+
+    struct S52
+    {
+      enum E52 { A52, B52, C52 };
+      union U52
+      {
+	int a;
+	char c;
+      };
+      int i52;
+      E52 e52;
+      U52 u52;
+
+      struct S53
+      {
+	enum E53 { A53, B53, C53 };
+	union U53
+	{
+	  int a;
+	  char c;
+	};
+	int i53;
+	E53 e53;
+	U53 u53;
+
+	struct S54
+	{
+	  enum E54 { A54, B54, C54 };
+	  union U54
+	  {
+	    int a;
+	    char c;
+	  };
+	  int i54;
+	  E54 e54;
+	  U54 u54;
+
+	  struct S55
+	  {
+	    enum E55 { A55, B55, C55 };
+	    union U55
+	    {
+	      int a;
+	      char c;
+	    };
+	    int i55;
+	    E55 e55;
+	    U55 u55;
+
+	    struct S56
+	    {
+	      enum E56 { A56, B56, C56 };
+	      union U56
+	      {
+		int a;
+		char c;
+	      };
+	      int i56;
+	      E56 e56;
+	      U56 u56;
+
+	      struct S57
+	      {
+		enum E57 { A57, B57, C57 };
+		union U57
+		{
+		  int a;
+		  char c;
+		};
+		int i57;
+		E57 e57;
+		U57 u57;
+
+		struct S58
+		{
+		  enum E58 { A58, B58, C58 };
+		  union U58
+		  {
+		    int a;
+		    char c;
+		  };
+		  int i58;
+		  E58 e58;
+		  U58 u58;
+
+		  struct S59
+		  {
+		    enum E59 { A59, B59, C59 };
+		    union U59
+		    {
+		      int a;
+		      char c;
+		    };
+		    int i59;
+		    E59 e59;
+		    U59 u59;
+		  };
+		};
+	      };
+	    };
+	  };
+	};
+      };
+    };
+  };
+};
+
+int
+main ()
+{
+  S10 s10;
+  S10::S11 s11;
+  S10::S11::S12 s12;
+  S10::S11::S12::S13 s13;
+  S10::S11::S12::S13::S14 s14;
+  S10::S11::S12::S13::S14::S15 s15;
+  S10::S11::S12::S13::S14::S15::S16 s16;
+  S10::S11::S12::S13::S14::S15::S16::S17 s17;
+  S10::S11::S12::S13::S14::S15::S16::S17::S18 s18;
+  S10::S11::S12::S13::S14::S15::S16::S17::S18::S19 s19;
+  S10::S21 s21;
+  S10::S21::S22 s22;
+  S10::S21::S22::S23 s23;
+  S10::S21::S22::S23::S24 s24;
+  S10::S21::S22::S23::S24::S25 s25;
+  S10::S21::S22::S23::S24::S25::S26 s26;
+  S10::S21::S22::S23::S24::S25::S26::S27 s27;
+  S10::S21::S22::S23::S24::S25::S26::S27::S28 s28;
+  S10::S21::S22::S23::S24::S25::S26::S27::S28::S29 s29;
+  S10::S31 s31;
+  S10::S31::S32 s32;
+  S10::S31::S32::S33 s33;
+  S10::S31::S32::S33::S34 s34;
+  S10::S31::S32::S33::S34::S35 s35;
+  S10::S31::S32::S33::S34::S35::S36 s36;
+  S10::S31::S32::S33::S34::S35::S36::S37 s37;
+  S10::S31::S32::S33::S34::S35::S36::S37::S38 s38;
+  S10::S31::S32::S33::S34::S35::S36::S37::S38::S39 s39;
+  S10::S41 s41;
+  S10::S41::S42 s42;
+  S10::S41::S42::S43 s43;
+  S10::S41::S42::S43::S44 s44;
+  S10::S41::S42::S43::S44::S45 s45;
+  S10::S41::S42::S43::S44::S45::S46 s46;
+  S10::S41::S42::S43::S44::S45::S46::S47 s47;
+  S10::S41::S42::S43::S44::S45::S46::S47::S48 s48;
+  S10::S41::S42::S43::S44::S45::S46::S47::S48::S49 s49;
+  S10::S51 s51;
+  S10::S51::S52 s52;
+  S10::S51::S52::S53 s53;
+  S10::S51::S52::S53::S54 s54;
+  S10::S51::S52::S53::S54::S55 s55;
+  S10::S51::S52::S53::S54::S55::S56 s56;
+  S10::S51::S52::S53::S54::S55::S56::S57 s57;
+  S10::S51::S52::S53::S54::S55::S56::S57::S58 s58;
+  S10::S51::S52::S53::S54::S55::S56::S57::S58::S59 s59;
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/nested-types.exp b/gdb/testsuite/gdb.cp/nested-types.exp
new file mode 100644
index 0000000000..a96493ff51
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nested-types.exp
@@ -0,0 +1,267 @@
+# Copyright 2017 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/>.
+
+# Test nested class definitions with the type printer.
+#
+# This test works by constructing a tree to represent "struct S10" in
+# the corresponding source file.  It then walks the nodes of this tree
+# to construct input suitable for passing to cp_test_ptype_class.
+
+if {[skip_cplus_tests]} { continue }
+
+load_lib "cp-support.exp"
+
+standard_testfile .cc
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
+	 {debug c++}]} {
+    return -1
+}
+
+# Build the node given by ID (a number representing the struct S[ID] in
+# the source file).
+#
+# For each node, stored as ::nodes(ID,ARG), where ARG is
+#
+# fields   - list of fields [no children]
+# children - list of types [children]
+
+proc build_node {id} {
+    global nodes
+
+    # For any node, FIELDS is always the types i(N), e(N), u(N)
+    # CHILDREN is a list of nodes called [E(N), U(N)] S(N+1)
+    #
+    # The root (10) also has S(N+11), S(N+21), S(N+31), S(N+41)
+
+    set nodes($id,fields) [list "int i$id" "E$id e$id" "U$id u$id"]
+    set ndoes($id,children) {}
+    if {$id == 10} {
+	set limit 5
+    } else {
+	set limit 1
+    }
+    for {set i 0} {$i < $limit} {incr i} {
+	set n [expr {1 + $id + $i * 10}]
+
+	# We don't build nodes which are multiples of 10
+	# (the source only uses that at the root struct).
+	# We also don't create nodes not in the source file
+	# (id >= 60).
+	if {[expr {$n % 10}] != 0 && $n < 60} {
+	    lappend nodes($id,children) $n
+	}
+    }
+}
+
+# A helper procedure to indent the log output by LVL.  This is used for
+# debugging the tree, if ever necessary.
+
+proc indent {lvl} {
+    for {set i 0} {$i < $lvl} {incr i} {
+	send_log "  "
+    }
+}
+
+# For the given CHILD name and PARENT_LIST, return the fully qualified
+# name of the child type.
+
+proc qual_name {child parent_list} {
+    if {[string range $child 0 2] != "int" && [llength $parent_list]} {
+	return "[join $parent_list ::]::$child"
+    } else {
+	return "$child"
+    }
+}
+
+# Output to the log and/or create the result list for the fields of node ID.
+
+proc make_fields {result_var id parent_list indent_lvl log} {
+    upvar $result_var result
+    global nodes
+
+    foreach type $nodes($id,fields) {
+	set s "[qual_name $type $parent_list];"
+	if {$log} {
+	    indent $indent_lvl
+	    send_log "$s\n"
+	}
+	lappend result [list "field" "public" "$s"]
+    }
+}
+
+# Output to the log and/or create the result list for the union type in
+# node ID.
+
+proc make_union {result_var id parent_list indent_lvl log} {
+    upvar $result_var result
+
+    set s "[qual_name U$id $parent_list]"
+    set a "int a;"
+    set c "char c;"
+    lappend result [list "type" "public" "union" $s [list $a $c]]
+    if {$log} {
+	indent $indent_lvl
+	send_log "union $s \{\n"
+	indent [expr {$indent_lvl + 1}]
+	send_log "$a\n"
+	indent [expr {$indent_lvl + 1}]
+	send_log "$c\n"
+	indent $indent_lvl
+	send_log "\};\n"
+    }
+}
+
+# Output to the log and/or create the result list for the enum type in
+# node ID.
+
+proc make_enum {result_var id parent_list indent_lvl log} {
+    upvar $result_var result
+
+    set s "[qual_name E$id $parent_list]"
+    set a "[qual_name A$id $parent_list]"
+    set b "[qual_name B$id $parent_list]"
+    set c "[qual_name C$id $parent_list]"
+    lappend result [list "type" "public" "enum" $s [list $a $b $c]]
+
+    if {$log} {
+	indent $indent_lvl
+	send_log "enum $s \{$a, $b, $c\};\n"
+    }
+}
+
+# Output to the log and/or create the result list for the node given by ID.
+#
+# LIMIT describes the number of nested types to output (corresponding to
+# the "set print type nested-type-limit" command).
+# PARENT_LIST is the list of parent nodes already seen.
+# INDENT_LVL is the indentation level (used when LOG is true).
+
+proc node_result {result_var id limit parent_list indent_lvl log} {
+    upvar $result_var result
+
+    # Start a new type list.
+    set my_name "S$id"
+    set s "[qual_name $my_name $parent_list]"
+    set my_result [list "type" "public" "struct" $s]
+
+    if {$log} {
+	indent $indent_lvl
+	send_log "struct $s \{\n"
+    }
+
+    # Add this node to the parent list so that its name appears in
+    # qualified names.
+    lappend parent_list "$my_name"
+
+    # Output field list to a local children list.
+    set children_list {}
+    make_fields children_list $id $parent_list [expr {$indent_lvl + 1}] $log
+
+    # Output type definitions to the local children list.
+    # The first number of ID gives us the depth of the node.
+    if {[string index $id 1] < $limit || $limit < 0} {
+	if {$log} {
+	    send_log "\n"
+	}
+	make_enum children_list $id $parent_list [expr {$indent_lvl + 1}] $log
+	make_union children_list $id $parent_list [expr {$indent_lvl + 1}] $log
+    }
+
+    # Output the children to the local children list.
+    global nodes
+    if {[info exists nodes($id,children)]} {
+	foreach c $nodes($id,children) {
+	    if {[string index $c 1] <= $limit || $limit < 0} {
+		node_result children_list $c $limit $parent_list \
+		    [expr {$indent_lvl + 1}] $log
+	    }
+	}
+    }
+
+    # Add this node's children to its result and add its result to
+    # its parent's results.
+    lappend my_result $children_list
+    lappend result $my_result
+
+    if {$log} {
+	indent $indent_lvl
+	send_log "\};\n"
+    }
+}
+
+# Test nested type definitions.  LIMIT specifies how many nested levels
+# of definitions to test.  If LOG is true, output the tree to the log in
+# a human-readable format mimicing the source code.
+
+proc test_nested_limit {limit log} {
+    set result {}
+
+    # Set the number of nested definitions to print.
+    gdb_test_no_output "set print type nested-type-limit $limit"
+
+    # Check the output of "show type print nested-type-limit"
+    if {$limit < 0} {
+	set lstr "unlimited"
+    } else {
+	set lstr $limit
+    }
+    gdb_test "show print type nested-type-limit" \
+	"Will print $lstr nested types defined in a class" \
+	"show print type nested-type-limit ($limit)"
+
+    # Generate the result list and test it.
+    if {$log} {
+	send_log "Tree to $limit levels:\n"
+    }
+    node_result result 10 $limit {} 0 $log
+
+    # The only output we check for is the contents of the struct, ignoring
+    # the leading "type = struct S10 {" and trailing "}" of the outermost node.
+    set result [lindex $result 0]
+    lassign $result type access key name children
+    cp_test_ptype_class $name "ptype $name (limit = $limit)" $key \
+	$name $children
+}
+
+# Build a tree of nodes describing the structures in the source file.
+
+# An array holding all the nodes
+array set nodes {}
+build_node 10
+for {set i 1} {$i < 6} {incr i} {
+    for {set j 1} {$j < 10} {incr j} {
+	build_node $i$j
+    }
+}
+
+# Check relevant commands.
+
+# By default, we do not print nested type definitions.
+gdb_test "show print type nested-type-limit" \
+    "Will not print nested types defined in a class" \
+    "show default print type nested-type-limit"
+
+# -1 means we print all nested types
+test_nested_limit -1 false
+
+# Test the output of "show print type nested-type-limit" and
+# ptype on the test source.
+
+for {set i 1} {$i < 9} {incr i} {
+    test_nested_limit $i false
+}
+
+unset -nocomplain nodes result
diff --git a/gdb/testsuite/lib/cp-support.exp b/gdb/testsuite/lib/cp-support.exp
index 5291921673..40a7088477 100644
--- a/gdb/testsuite/lib/cp-support.exp
+++ b/gdb/testsuite/lib/cp-support.exp
@@ -15,6 +15,15 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+load_lib "data-structures.exp"
+
+# Controls whether detailed logging for cp_test_ptype_class is enabled.
+# By default, it is not.  Enable it to assist with troubleshooting
+# failed cp_test_ptype_class tests.  [Users can simply add the statement
+# "set debug_cp_ptype_test_class true" after this file is loaded.]
+
+set ::debug_cp_test_ptype_class false
+
 # Auxiliary function to check for known problems.
 #
 # EXPECTED_STRING is the string expected by the test.
@@ -38,7 +47,37 @@ proc cp_check_errata { expected_string actual_string errata_table } {
     }
 }
 
-# Test ptype of a class.
+# A convenience procedure for outputting debug info for cp_test_ptype_class
+# to the log.  Set the global variable "debug_cp_test_ptype_class"
+# to enable logging (to help with debugging failures).
+
+proc cp_ptype_class_verbose {msg} {
+    global debug_cp_test_ptype_class
+
+    if {$debug_cp_test_ptype_class} {
+	verbose -log $msg
+    }
+}
+
+# A convenience procedure to return the next element of the queue.
+
+proc next_line {qid} {
+    set elem {}
+
+    while {$elem == "" && ![queue empty $qid]} {
+	# We make cp_test_ptype_class trim whitespace
+	set elem [queue pop $qid]
+    }
+
+    if {$elem == ""} {
+	cp_ptype_class_verbose "next line element: no more lines"
+    } else {
+	cp_ptype_class_verbose "next line element: \"$elem\""
+    }
+    return $elem
+}
+
+# Test ptype of a class.  Return `true' if the test passes, false otherwise.
 #
 # Different C++ compilers produce different output.  To accommodate all
 # the variations listed below, I read the output of "ptype" and process
@@ -87,6 +126,20 @@ proc cp_check_errata { expected_string actual_string errata_table } {
 #      the class has a typedef with the given access type and the
 #      given declaration.
 #
+#   { type "access" "key" "name" children }
+#
+#      The class has a nested type definition with the given ACCESS.
+#      KEY is the keyword of the nested type ("enum", "union", "struct",
+#         "class").
+#      NAME is the (tag) name of the type.
+#      CHILDREN is a list of the type's children.  For struct and union keys,
+#        this is simply the same type of list that is normally passed to
+#        this procedure.  For enums the list of children should be the
+#        defined enumerators.  For unions it is a list of declarations.
+#        NOTE: The enum key will add a regexp to handle optional storage
+#        class specifiers (": unsigned int", e.g.).  The caller need not
+#        specify this.
+#
 # If you test the same class declaration more than once, you can specify
 # IN_CLASS_TABLE as "ibid".  "ibid" means: look for a previous class
 # table that had the same IN_KEY and IN_TAG, and re-use that table.
@@ -102,6 +155,11 @@ proc cp_check_errata { expected_string actual_string errata_table } {
 # 
 # IN_PTYPE_ARG are arguments to pass to ptype.  The default is "/r".
 #
+# RECURSIVE_QID is used internally to call this procedure recursively
+# when, e.g., testing nested type definitions.  The "ptype" command will
+# not be sent to GDB and the lines in the queue given by this argument will
+# be used instead.
+#
 # gdb can vary the output of ptype in several ways:
 #
 # . CLASS/STRUCT
@@ -178,16 +236,20 @@ 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 { } } { in_ptype_arg /r } } {
+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 } { recursive_qid 0 } } {
     global gdb_prompt
     set wsopt "\[\r\n\t \]*"
 
-    # The test name defaults to the command, but without the
-    # arguments, for historical reasons.
+    if {$recursive_qid == 0} {
+	# The test name defaults to the command, but without the
+	# arguments, for historical reasons.
 
-    if { "$in_testname" == "" } then { set in_testname "ptype $in_exp" }
+	if { "$in_testname" == "" } then { set in_testname "ptype $in_exp" }
 
-    set in_command "ptype${in_ptype_arg} $in_exp"
+	set in_command "ptype${in_ptype_arg} $in_exp"
+    }
 
     # Save class tables in a history array for reuse.
 
@@ -195,7 +257,7 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     if { $in_class_table == "ibid" } then {
 	if { ! [info exists cp_class_table_history("$in_key,$in_tag") ] } then {
 	    fail "$in_testname // bad ibid"
-	    return
+	    return false
 	}
 	set in_class_table $cp_class_table_history("$in_key,$in_tag")
     } else {
@@ -209,6 +271,9 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     set list_fields  { }
     set list_methods { }
     set list_typedefs { }
+    set list_types    { }
+    set list_enums    { }
+    set list_unions   { }
 
     foreach class_line $in_class_table {
 	switch [lindex $class_line 0] {
@@ -217,7 +282,11 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	    "field"  { lappend list_fields  [lrange $class_line 1 2] }
 	    "method" { lappend list_methods [lrange $class_line 1 2] }
 	    "typedef" { lappend list_typedefs [lrange $class_line 1 2] }
-	    default  { fail "$in_testname // bad line in class table: $class_line"; return; }
+	    "type"    { lappend list_types [lrange $class_line 1 4] }
+	    default  {
+		fail "$in_testname // bad line in class table: $class_line"
+		return false
+	    }
 	}
     }
 
@@ -225,24 +294,56 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     # These are: { count ccess-type regular-expression }.
 
     set list_synth { }
-    lappend list_synth [list 0 "public" "$in_tag & operator=\\($in_tag const ?&\\);"]
-    lappend list_synth [list 0 "public" "$in_tag\\((int,|) ?$in_tag const ?&\\);"]
-    lappend list_synth [list 0 "public" "$in_tag\\((int|void|)\\);"]
-
-    # Actually do the ptype.
-
-    set parse_okay 0
-    gdb_test_multiple "$in_command" "$in_testname // parse failed" {
-	-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(4,string)
-	    set actual_body         $expect_out(6,string)
-	    set actual_tail         $expect_out(7,string)
+    lappend list_synth [list 0 "public" \
+			    "$in_tag & operator=\\($in_tag const ?&\\);"]
+    lappend list_synth [list 0 "public" \
+			    "$in_tag\\((int,|) ?$in_tag const ?&\\);"]
+    lappend list_synth [list 0 "public" \
+			    "$in_tag\\((int|void|)\\);"]
+
+    # Partial regexp for parsing the struct/class header.
+    set regexp_header "(struct|class)${wsopt}(\[^ \t\]*)${wsopt}"
+    append regexp_header "(\\\[with .*\\\]${wsopt})?((:\[^\{\]*)?)${wsopt}\{"
+    if {$recursive_qid == 0} {
+	# Actually do the ptype.
+
+	# For processing the output of ptype, we must get to the prompt.
+	set the_regexp "type = ${regexp_header}"
+	append the_regexp "(.*)\}${wsopt}(\[^\r\n\]*)\[\r\n\]+$gdb_prompt $"
+	set parse_okay 0
+	gdb_test_multiple "$in_command" "$in_testname // parse failed" {
+	    -re $the_regexp {
+		set parse_okay          1
+		set actual_key          $expect_out(1,string)
+		set actual_tag          $expect_out(2,string)
+		set actual_base_string  $expect_out(4,string)
+		set actual_body         $expect_out(6,string)
+		set actual_tail         $expect_out(7,string)
+	    }
+	}
+    } else {
+	# The struct/class header by the first element in the line queue.
+	# "Parse" that instead of the output of ptype.
+	set header [next_line $recursive_qid]
+	set parse_okay [regexp $regexp_header $header dummy actual_key \
+			    actual_tag dummy actual_base_string]
+
+	if {$parse_okay} {
+	    cp_ptype_class_verbose \
+		"Parsing nested type definition (parse_okay=$parse_okay):"
+	    cp_ptype_class_verbose \
+		"\tactual_key=$actual_key, actual_tag=$actual_tag"
+	    cp_ptype_class_verbose "\tactual_base_string=$actual_base_string"
 	}
+
+	# Cannot have a tail with a nested type definition.
+	set actual_tail ""
+    }
+
+    if { ! $parse_okay } {
+	cp_ptype_class_verbose "*** parse failed ***"
+	return false
     }
-    if { ! $parse_okay } then { return }
 
     # Check the actual key.  It would be nice to require that it match
     # the input key, but gdb does not support that.  For now, accept any
@@ -256,7 +357,7 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	    cp_check_errata "class"  "$actual_key" $in_errata_table
 	    cp_check_errata "struct" "$actual_key" $in_errata_table
 	    fail "$in_testname // wrong key: $actual_key"
-	    return
+	    return false
 	}
     }
 
@@ -265,7 +366,7 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     if { "$actual_tag" != "$in_tag" } then {
 	cp_check_errata "$in_tag" "$actual_tag" $in_errata_table
 	fail "$in_testname // wrong tag: $actual_tag"
-	return
+	return false
     }
 
     # Check the actual bases.
@@ -281,11 +382,11 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 
     if { [llength $list_actual_bases] < [llength $list_bases] } then {
 	fail "$in_testname // too few bases"
-	return
+	return false
     }
     if { [llength $list_actual_bases] > [llength $list_bases] } then {
 	fail "$in_testname // too many bases"
-	return
+	return false
     }
 
     # Check each base.
@@ -296,7 +397,7 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	if { "$actual_base" != "$base" } then {
 	    cp_check_errata "$base" "$actual_base" $in_errata_table
 	    fail "$in_testname // wrong base: $actual_base"
-	    return
+	    return false
 	}
 	set list_bases [lreplace $list_bases 0 0]
     }
@@ -306,11 +407,26 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     set last_was_access 0
     set vbase_match 0
 
-    foreach actual_line [split $actual_body "\r\n"] {
+    if {$recursive_qid == 0} {
+	# Use a queue to hold the lines that will be checked.
+	# This will allow processing below to remove lines from the input
+	# more easily.
+	set line_queue [::Queue::new]
+	foreach l [split $actual_body "\r\n"] {
+	    set l [string trim $l]
+	    if {$l != ""} {
+		queue push $line_queue $l
+	    }
+	}
+    } else {
+	set line_queue $recursive_qid
+    }
 
-	# Chomp the line.
+    while {![queue empty $line_queue]} {
 
-	set actual_line [string trim $actual_line]
+	# Get the next line.
+
+	set actual_line [next_line $line_queue]
 	if { "$actual_line" == "" } then { continue }
 
 	# Access specifiers.
@@ -319,7 +435,8 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	    set access "$s1"
 	    if { $last_was_access } then {
 		fail "$in_testname // redundant access specifier"
-		return
+		queue delete $line_queue
+		return false
 	    }
 	    set last_was_access 1
 	    continue
@@ -335,7 +452,8 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 		if { "$access" != "private" } then {
 		    cp_check_errata "private" "$access" $in_errata_table
 		    fail "$in_testname // wrong access specifier for virtual base: $access"
-		    return
+		    queue delete $line_queue
+		    return false
 		}
 		set list_vbases [lreplace $list_vbases 0 0]
 		set vbase_match 1
@@ -348,11 +466,18 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	if { [llength $list_fields] > 0 } then {
 	    set field_access [lindex [lindex $list_fields 0] 0]
 	    set field_decl   [lindex [lindex $list_fields 0] 1]
+	    if {$recursive_qid > 0} {
+		cp_ptype_class_verbose "\tactual_line=$actual_line"
+		cp_ptype_class_verbose "\tfield_access=$field_access"
+		cp_ptype_class_verbose "\tfield_decl=$field_decl"
+		cp_ptype_class_verbose "\taccess=$access"
+	    }
 	    if { "$actual_line" == "$field_decl" } then {
 		if { "$access" != "$field_access" } then {
 		    cp_check_errata "$field_access" "$access" $in_errata_table
 		    fail "$in_testname // wrong access specifier for field: $access"
-		    return
+		    queue delete $line_queue
+		    return false
 		}
 		set list_fields [lreplace $list_fields 0 0]
 		continue
@@ -361,7 +486,8 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	    # Data fields must appear before synths and methods.
 	    cp_check_errata "$field_decl" "$actual_line" $in_errata_table
 	    fail "$in_testname // unrecognized line type 1: $actual_line"
-	    return
+	    queue delete $line_queue
+	    return false
 	}
 
 	# Method function.
@@ -373,7 +499,8 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 		if { "$access" != "$method_access" } then {
 		    cp_check_errata "$method_access" "$access" $in_errata_table
 		    fail "$in_testname // wrong access specifier for method: $access"
-		    return
+		    queue delete $line_queue
+		    return false
 		}
 		set list_methods [lreplace $list_methods 0 0]
 		continue
@@ -385,7 +512,8 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 		if { "$access" != "$method_access" } then {
 		    cp_check_errata "$method_access" "$access" $in_errata_table
 		    fail "$in_testname // wrong access specifier for method: $access"
-		    return
+		    queue delete $line_queue
+		    return false
 		}
 		set list_methods [lreplace $list_methods 0 0]
 		continue
@@ -401,13 +529,129 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 		if {![string equal $access $typedef_access]} {
 		    cp_check_errata $typedef_access $access $in_errata_table
 		    fail "$in_testname // wrong access specifier for typedef: $access"
-		    return
+		    queue delete $line_queue
+		    return false
 		}
 		set list_typedefs [lreplace $list_typedefs 0 0]
 		continue
 	    }
 	}
 
+	# Nested type definitions
+
+	if {[llength $list_types] > 0} {
+	    cp_ptype_class_verbose "Nested type definition: "
+	    lassign [lindex $list_types 0] nested_access nested_key \
+		nested_name nested_children
+	    set msg "nested_access=$nested_access, nested_key=$nested_key, "
+	    append msg "nested_name=$nested_name, "
+	    append msg "[llength $nested_children] children"
+	    cp_ptype_class_verbose $msg
+
+	    if {![string equal $access $nested_access]} {
+		cp_check_errata $nested_access $access $in_errata_table
+		set txt "$in_testname // wrong access specifier for "
+		append txt "nested type: $access"
+		fail $txt
+		queue delete $line_queue
+		return false
+	    }
+
+	    switch $nested_key {
+		enum {
+		    set expected_result \
+			"enum $nested_name (: (unsigned )?int)? \{"
+		    foreach c $nested_children {
+			append expected_result "$c, "
+		    }
+		    set expected_result \
+			[string trimright $expected_result { ,}]
+		    append expected_result "\};"
+		    cp_ptype_class_verbose \
+			"Expecting enum result: $expected_result"
+		    if {![regexp -- $expected_result $actual_line]} {
+			set txt "$in_testname // wrong nested type enum"
+			append txt " definition: $actual_linejj"
+			fail $txt
+			queue delete $line_queue
+			return false
+		    }
+		    cp_ptype_class_verbose "passed enum $nested_name"
+		}
+
+		union {
+		    set expected_result "union $nested_name \{"
+		    cp_ptype_class_verbose \
+			"Expecting union result: $expected_result"
+		    if {![string equal $expected_result $actual_line]} {
+			set txt "$in_testname // wrong nested type union"
+			append txt " definition: $actual_line"
+			fail $txt
+			queue delete $line_queue
+			return false
+		    }
+
+		    # This will be followed by lines for each member of the
+		    # union.
+		    cp_ptype_class_verbose "matched union name"
+		    foreach m $nested_children {
+			set actual_line [next_line $line_queue]
+			cp_ptype_class_verbose "Expecting union member: $m"
+			if {![string equal $m $actual_line]} {
+			    set txt "$in_testname // unexpected union member: "
+			    append txt $m
+			    fail $txt
+			    queue delete $line_queue
+			    return false
+			}
+			cp_ptype_class_verbose "matched union child \"$m\""
+		    }
+
+		    # Nested union types always end with a trailing curly brace.
+		    set actual_line [next_line $line_queue]
+		    if {![string equal $actual_line "\};"]} {
+			fail "$in_testname // missing closing curly brace"
+			queue delete $line_queue
+			return false
+		    }
+		    cp_ptype_class_verbose "passed union $nested_name"
+		}
+
+		struct -
+		class {
+		    cp_ptype_class_verbose \
+			"Expecting [llength $nested_children] children"
+		    foreach c $nested_children {
+			cp_ptype_class_verbose "\t$c"
+		    }
+		    # Start by pushing the current line back into the queue
+		    # so that the recursive call can parse the class/struct
+		    # header.
+		    queue unpush $line_queue $actual_line
+		    cp_ptype_class_verbose \
+			"Recursing for type $nested_key $nested_name"
+		    if {![cp_test_ptype_class $in_exp $in_testname $nested_key \
+			      $nested_name $nested_children $in_tail \
+			      $in_errata_table $in_ptype_arg $line_queue]} {
+			# The recursive call has already called `fail' and
+			# released the line queue.
+			return false
+		    }
+		    cp_ptype_class_verbose \
+			"passed nested type $nested_key $nested_name"
+		}
+
+		default {
+		    fail "$in_testname // invalid nested type key: $nested_key"
+		    queue delete $line_queue
+		    return false
+		}
+	    }
+
+	    set list_types [lreplace $list_types 0 0]
+	    continue
+	}
+
 	# Synthetic operators.  These are optional and can be mixed in
 	# with the methods in any order, but duplicates are wrong.
 	#
@@ -427,7 +671,8 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 		if { "$access" != "$synth_access" } then {
 		    cp_check_errata "$synth_access" "$access" $in_errata_table
 		    fail "$in_testname // wrong access specifier for synthetic operator: $access"
-		    return
+		    queue delete $line_queue
+		    return false
 		}
 
 		if { $synth_count > 0 } then {
@@ -449,6 +694,12 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	}
 	if { $synth_match } then { continue }
 
+	# If checking a nested type/recursively and we see a closing curly
+	# brace, we're done.
+	if {$recursive_qid != 0 && [string equal $actual_line "\};"]} {
+	    break
+	}
+
 	# Unrecognized line.
 
 	if { [llength $list_methods] > 0 } then {
@@ -457,7 +708,13 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
 	}
 
 	fail "$in_testname // unrecognized line type 2: $actual_line"
-	return
+	queue delete $line_queue
+	return false
+    }
+
+    # Done with the line queue.
+    if {$recursive_qid == 0} {
+	queue delete $line_queue
     }
 
     # Check for missing elements.
@@ -465,23 +722,23 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     if { $vbase_match } then {
 	if { [llength $list_vbases] > 0 } then {
 	    fail "$in_testname // missing virtual base pointers"
-	    return
+	    return false
 	}
     }
 
     if { [llength $list_fields] > 0 } then {
 	fail "$in_testname // missing fields"
-	return
+	return false
     }
 
     if { [llength $list_methods] > 0 } then {
 	fail "$in_testname // missing methods"
-	return
+	return false
     }
 
     if {[llength $list_typedefs] > 0} {
 	fail "$in_testname // missing typedefs"
-	return
+	return false
     }
 
     # Check the tail.
@@ -490,11 +747,15 @@ proc cp_test_ptype_class { in_exp in_testname in_key in_tag in_class_table { in_
     if { "$actual_tail" != "$in_tail" } then {
 	cp_check_errata "$in_tail" "$actual_tail" $in_errata_table
 	fail "$in_testname // wrong tail: $actual_tail"
-	return
+	return false
     }
 
-    # It all worked!
+    # It all worked, but don't call `pass' if we've been called
+    # recursively.
+
+    if {$recursive_qid == 0} {
+	pass "$in_testname"
+    }
 
-    pass "$in_testname"
-    return
+    return true
 }
diff --git a/gdb/testsuite/lib/data-structures.exp b/gdb/testsuite/lib/data-structures.exp
new file mode 100644
index 0000000000..a44978be3a
--- /dev/null
+++ b/gdb/testsuite/lib/data-structures.exp
@@ -0,0 +1,160 @@
+# Copyright 2017 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/>.
+
+# This file implements some simple data structures in Tcl.
+
+# A namespace/commands to support a stack.
+#
+# To create a stack, call ::Stack::new, recording the returned object ID
+# for future calls to manipulate the stack object.
+#
+# Example:
+#
+# set sid1 [::Stack::new]
+# set sid2 [::Stack::new]
+# stack push $sid1 a
+# stack push $sid1 b
+# stack empty $sid1;  # returns false
+# stack empty $sid2;  # returns true
+# stack pop $sid1;    # returns "b"
+# stack pop $sid2;    # returns ""
+# stack delete $sid1
+# stack delete $sid2
+
+namespace eval ::Stack {
+    # A counter used to create object IDs
+    variable num_ 0
+
+    # An array holding all object lists, indexed by object ID.
+    variable data_
+
+    # Create a new stack object, returning its object ID.
+    proc new {} {
+	variable num_
+	variable data_
+
+	set oid [incr num_]
+	set data_($oid) [list]
+	return $oid
+    }
+
+    # Delete the given stack ID.
+    proc delete {oid} {
+	variable data_
+
+	error_if $oid
+	unset data_($oid)
+    }
+
+    # Returns whether the given stack is empty.
+    proc empty {oid} {
+	variable data_
+
+	error_if $oid
+	return [expr {[llength $data_($oid)] == 0}]
+    }
+
+    # Push ELEM onto the stack given by OID.
+    proc push {oid elem} {
+	variable data_
+
+	error_if $oid
+	lappend data_($oid) $elem
+    }
+
+    # Return and pop the top element on OID.
+    proc pop {oid} {
+	variable data_
+
+	error_if $oid
+	set elem [lindex $data_($oid) end]
+	set data_($oid) [lreplace $data_($oid) end end]
+	return $elem
+    }
+
+    # Returns the depth of a given ID.
+    proc length {oid} {
+	variable data_
+
+	error_if $oid
+	return [llength $data_($oid)]
+    }
+
+    # Error handler for invalid object IDs.
+    proc error_if {oid} {
+	variable data_
+
+	if {![info exists data_($oid)]} {
+	    ::error "object ID $oid does not exist"
+	}
+    }
+
+    # Export procs to be used.
+    namespace export empty push pop new delete length error_if
+
+    # Create an ensemble command to use instead of requiring users
+    # to type namespace proc names.
+    namespace ensemble create -command ::stack
+}
+
+# A namespace/commands to support a queue.
+#
+# To create a queue, call ::Queue::new, recording the returned stack ID
+# for future calls to manipulate the queue object.
+#
+# Example:
+#
+# set qid1 [::Queue::new]
+# set qid2 [::Queue::new]
+# queue push $qid1 a
+# queue push $qid1 b
+# queue empty $qid1;  # returns false
+# queue empty $qid2;  # returns true
+# queue pop $qid1;    # returns "a"
+# queue pop $qid2;    # returns ""
+# queue delete $qid1
+# queue delete $qid2
+
+namespace eval ::Queue {
+
+    # Remove and return the oldest element in the queue given by OID.
+    proc pop {oid} {
+	variable ::Stack::data_
+
+	error_if $oid
+	set elem [lindex $data_($oid) 0]
+	set data_($oid) [lreplace $data_($oid) 0 0]
+	return $elem
+    }
+
+    # "Unpush" ELEM back to the head of the queue given by QID.
+    proc unpush {oid elem} {
+	variable ::Stack::data_
+
+	error_if $oid
+	set data_($oid) [linsert $data_($oid) 0 $elem]
+    }
+
+    # Re-use some common routines from the Stack implementation.
+    namespace import ::Stack::create ::Stack::new ::Stack::empty \
+	::Stack::delete ::Stack::push ::Stack::length ::Stack::error_if
+
+    # Export procs to be used.
+    namespace export new empty push pop new delete length error_if unpush
+
+    # Create an ensemble command to use instead of requiring users
+    # to type namespace proc names.
+    namespace ensemble create -command ::queue
+}
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 441cbb8742..ef236fef02 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -48,6 +48,7 @@ const struct type_print_options type_print_raw_options =
   1,				/* raw */
   1,				/* print_methods */
   1,				/* print_typedefs */
+  0,				/* print_nested_type_limit  */
   NULL,				/* local_typedefs */
   NULL,				/* global_table */
   NULL				/* global_printers */
@@ -60,6 +61,7 @@ static struct type_print_options default_ptype_flags =
   0,				/* raw */
   1,				/* print_methods */
   1,				/* print_typedefs */
+  0,				/* print_nested_type_limit  */
   NULL,				/* local_typedefs */
   NULL,				/* global_table */
   NULL				/* global_printers */
@@ -85,7 +87,7 @@ struct typedef_hash_table
 static hashval_t
 hash_typedef_field (const void *p)
 {
-  const struct typedef_field *tf = (const struct typedef_field *) p;
+  const struct decl_field *tf = (const struct decl_field *) p;
   struct type *t = check_typedef (tf->type);
 
   return htab_hash_string (TYPE_SAFE_NAME (t));
@@ -96,8 +98,8 @@ hash_typedef_field (const void *p)
 static int
 eq_typedef_field (const void *a, const void *b)
 {
-  const struct typedef_field *tfa = (const struct typedef_field *) a;
-  const struct typedef_field *tfb = (const struct typedef_field *) b;
+  const struct decl_field *tfa = (const struct decl_field *) a;
+  const struct decl_field *tfb = (const struct decl_field *) b;
 
   return types_equal (tfa->type, tfb->type);
 }
@@ -115,7 +117,7 @@ recursively_update_typedef_hash (struct typedef_hash_table *table,
 
   for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (t); ++i)
     {
-      struct typedef_field *tdef = &TYPE_TYPEDEF_FIELD (t, i);
+      struct decl_field *tdef = &TYPE_TYPEDEF_FIELD (t, i);
       void **slot;
 
       slot = htab_find_slot (table->table, tdef, INSERT);
@@ -143,14 +145,14 @@ add_template_parameters (struct typedef_hash_table *table, struct type *t)
 
   for (i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (t); ++i)
     {
-      struct typedef_field *tf;
+      struct decl_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 = XOBNEW (&table->storage, struct decl_field);
       tf->name = SYMBOL_LINKAGE_NAME (TYPE_TEMPLATE_ARGUMENT (t, i));
       tf->type = SYMBOL_TYPE (TYPE_TEMPLATE_ARGUMENT (t, i));
 
@@ -268,7 +270,7 @@ find_global_typedef (const struct type_print_options *flags,
 {
   char *applied;
   void **slot;
-  struct typedef_field tf, *new_tf;
+  struct decl_field tf, *new_tf;
 
   if (flags->global_typedefs == NULL)
     return NULL;
@@ -279,13 +281,13 @@ find_global_typedef (const struct type_print_options *flags,
   slot = htab_find_slot (flags->global_typedefs->table, &tf, INSERT);
   if (*slot != NULL)
     {
-      new_tf = (struct typedef_field *) *slot;
+      new_tf = (struct decl_field *) *slot;
       return new_tf->name;
     }
 
   /* Put an entry into the hash table now, in case
      apply_ext_lang_type_printers recurses.  */
-  new_tf = XOBNEW (&flags->global_typedefs->storage, struct typedef_field);
+  new_tf = XOBNEW (&flags->global_typedefs->storage, struct decl_field);
   new_tf->name = NULL;
   new_tf->type = t;
 
@@ -314,12 +316,12 @@ find_typedef_in_hash (const struct type_print_options *flags, struct type *t)
 {
   if (flags->local_typedefs != NULL)
     {
-      struct typedef_field tf, *found;
+      struct decl_field tf, *found;
 
       tf.name = NULL;
       tf.type = t;
-      found = (struct typedef_field *) htab_find (flags->local_typedefs->table,
-						  &tf);
+      found = (struct decl_field *) htab_find (flags->local_typedefs->table,
+					       &tf);
 
       if (found != NULL)
 	return found->name;
@@ -707,6 +709,41 @@ show_print_type_typedefs (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* Limit on the number of nested type definitions to print or -1 to print
+   all nested type definitions in a class.  By default, we do not print
+   nested definitions.  */
+
+static int print_nested_type_limit = 0;
+
+/* Set how many nested type definitions should be printed by the type
+   printer.  */
+
+static void
+set_print_type_nested_types (char *args, int from_tty,
+			     struct cmd_list_element *c)
+{
+  default_ptype_flags.print_nested_type_limit = print_nested_type_limit;
+}
+
+/* Show how many nested type definitions the type printer will print.  */
+
+static void
+show_print_type_nested_types  (struct ui_file *file, int from_tty,
+			       struct cmd_list_element *c, const char *value)
+{
+  if (*value == '0')
+    {
+      fprintf_filtered (file,
+			_("Will not print nested types defined in a class\n"));
+    }
+  else
+    {
+      fprintf_filtered (file,
+			_("Will print %s nested types defined in a class\n"),
+			value);
+    }
+}
+
 void
 _initialize_typeprint (void)
 {
@@ -755,6 +792,16 @@ Show printing of typedefs defined in classes."), NULL,
 			   set_print_type_typedefs,
 			   show_print_type_typedefs,
 			   &setprinttypelist, &showprinttypelist);
+
+  add_setshow_zuinteger_unlimited_cmd ("nested-type-limit", no_class,
+				       &print_nested_type_limit,
+				       _("\
+Set the number of recursive nested type definitions to print \
+(-1 to show all)."), _("\
+Show the number of recursive nested type definitions to print."), NULL,
+				       set_print_type_nested_types,
+				       show_print_type_nested_types,
+				       &setprinttypelist, &showprinttypelist);
 }
 
 /* Print <not allocated> status to stream STREAM.  */
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index a458aa4e2f..a2058b0120 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -35,6 +35,9 @@ struct type_print_options
   /* True means print typedefs in a class.  */
   unsigned int print_typedefs : 1;
 
+  /* The number of nested type definitions to print.  -1 == all.  */
+  int print_nested_type_limit;
+
   /* If not NULL, a local typedef hash table used when printing a
      type.  */
   struct typedef_hash_table *local_typedefs;
