From patchwork Fri Oct 20 18:26:48 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Seitz X-Patchwork-Id: 23736 Received: (qmail 90688 invoked by alias); 20 Oct 2017 18:26:59 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 90591 invoked by uid 89); 20 Oct 2017 18:26:58 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.8 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_STOCKGEN, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=Aid, Tcl, ij, displays X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 20 Oct 2017 18:26:50 +0000 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3341B13AB0 for ; Fri, 20 Oct 2017 18:26:49 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 3341B13AB0 Authentication-Results: ext-mx05.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx05.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=keiths@redhat.com Received: from theo.Home (ovpn04.gateway.prod.ext.phx2.redhat.com [10.5.9.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id E35FA5C88D for ; Fri, 20 Oct 2017 18:26:48 +0000 (UTC) From: Keith Seitz To: gdb-patches@sourceware.org Subject: [PATCH] Record nested types Date: Fri, 20 Oct 2017 11:26:48 -0700 Message-Id: <20171020182648.5093-1-keiths@redhat.com> X-IsSubscribed: yes GDB currently does not track types defined in classes. Consider: class A { public: class B { public: class C { }; }; }; (gdb) ptype A type = class A { } This patch changes this behavior so that GDB records these nested types and displays them to the user when he has set the (new) "print type" option "nested-type-limit." Example: (gdb) set print type nested-type-limit 1 (gdb) ptype A type = class A { class A::B { }; } (gdb) set print type nested-type-limit 2 type = class A { class A::B { class A::B::C { }; }; } By default, the code maintains the status quo, that is, it will not print any nested type definitions at all. Testing is carried out via cp_ptype_class which required quite a bit of modificaiton to permit recursive calling (for the nested types). This was most easily facilitated by turning the ptype command output into a queue. Upshot: the test suite now has stack and queue data structures that may be used by test writers. gdb/ChangeLog * NEWS (New commands): Mention set/show print type nested-type-limit. * c-typeprint.c (c_type_print_base): Print out nested types. * dwarf2read.c (struct typedef_field_list): Rename to ... (struct decl_field_list): ... this. Change all uses. (struct field_info) : New fields. (add_partial_symbol): Look for nested type definitions in C++, too. (dwarf2_add_typedef): Rename to ... (dwarf2_add_type_defn): ... this. Update assertion to allow classes, structures, unions, and enums. Permit NULL for a field's name. (process_structure_scope): Handle child DIEs of class, structure, union, and enum types. Copy the list of nested types into the type struct. * gdbtypes.h (struct typedef_field): Rename to ... (struct decl_field): ... this. Change all uses. [is_protected, is_private]: New fields. (struct cplus_struct_type) : New fields. (TYPE_NESTED_TYPES_ARRAY, TYPE_NESTED_TYPES_FIELD) (TYPE_NESTED_TYPES_FIELD_NAME, TYPE_NESTED_TYPES_FIELD_TYPE) (TYPE_NESTED_TYPES_COUNT, TYPE_NESTED_TYPES_FIELD_PUBLIC) (TYPE_NESTED_TYPES_FIELD_PROTECTED) (TYPE_NESTED_TYPES_FIELD_PRIVATE): New macros. * typeprint.c (type_print_raw_options, default_ptype_flags): Add default value for print_nested_type_limit. (print_nested_type_limit): New static variable. (set_print_type_nested_types, show_print_type_nested_types): New functions. (_initialize_typeprint): Register new commands for set/show `print-nested-type-limit'. * typeprint.h (struct type_print_options) [print_nested_type_limit]: New field. gdb/testsuite/ChangeLog * gdb.cp/nested-types.cc: New file. * gdb.cp/nested-types.exp: New file. * lib/cp-support.exp: Load data-structures.exp library. (debug_cp_test_ptype_class): New global. (cp_ptype_class_verbose, next_line): New procedures. (cp_test_ptype_class): Add and document new parameter `recursive_qid'. Add and document new return value. Switch the list of lines to a queue. Add support for new `type' key for nested type definitions. Add debugging/troubleshooting messages. * lib/data-structures.exp: New file. gdb/doc/ChangeLog * gdb.texinfo (Symbols): Document "set print type nested-type-limit" and "show print type nested-type-limit". --- gdb/NEWS | 4 + gdb/c-typeprint.c | 22 +- gdb/doc/gdb.texinfo | 11 + gdb/dwarf2read.c | 94 +++-- gdb/gdbtypes.h | 28 +- gdb/testsuite/gdb.cp/nested-types.cc | 668 ++++++++++++++++++++++++++++++++++ gdb/testsuite/gdb.cp/nested-types.exp | 267 ++++++++++++++ gdb/testsuite/lib/cp-support.exp | 359 +++++++++++++++--- gdb/testsuite/lib/data-structures.exp | 160 ++++++++ gdb/typeprint.c | 71 +++- gdb/typeprint.h | 3 + 11 files changed, 1598 insertions(+), 89 deletions(-) create mode 100644 gdb/testsuite/gdb.cp/nested-types.cc create mode 100644 gdb/testsuite/gdb.cp/nested-types.exp create mode 100644 gdb/testsuite/lib/data-structures.exp 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 . */ + +/* 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 . + +# 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 . +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 . + +# 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 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;