From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id eSRCK7HqyGrzfx8AWB0awg (envelope-from ) for ; Fri, 09 Oct 2026 09:22:57 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=g4/uNelF; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id AB3BE1E051; Fri, 09 Oct 2026 09:22:57 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 6E04B1E04E for ; Fri, 09 Oct 2026 09:22:55 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A8AE84BA2E2F for ; Fri, 9 Oct 2026 13:22:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A8AE84BA2E2F Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=g4/uNelF Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.15]) by sourceware.org (Postfix) with ESMTPS id 8A49A4BA2E2C for ; Fri, 9 Oct 2026 13:21:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8A49A4BA2E2C Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 8A49A4BA2E2C Authentication-Results: sourceware.org; arc=none smtp.remote-ip=198.175.65.15 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1791552118; cv=none; b=jayr+sWKwujPZ+CntIB4a7+eH9d62Z939+5YZPM0EIKiq+4fgm1BEA1cVSjGYky/Y8/+3wuFbea/uoH3O3T5+KtM6l5CefpYpkuv56oh7uGn2Y4IDySso22luQFGeUhyG2DK1PPMdXv6QeCXEKO/SSiYrxNDHxbwUeSU7jYln8o= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1791552118; c=relaxed/simple; bh=8ighNOu2OFJrM22IRDqDB7T1fHk75Rl2xP/qw090e4s=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=HeAPlIOTjY+CxMUfQgq05hl2reqw0Q7dcik53/c5x5GOMDPJqzTTTSHFs4KVGdGk7wTM76ER1nQ/TqeumYnSiqU6De40FWREf8tRqjhnHHRW+pVkesati023niHAo/kn+3d4uSeP6divJo+wC80b6BllE2bHELGl410d+mI8qS0= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=g4/uNelF DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8A49A4BA2E2C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1791552118; x=1823088118; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=8ighNOu2OFJrM22IRDqDB7T1fHk75Rl2xP/qw090e4s=; b=g4/uNelFcHxfBhcRKjbW5jafrEVjmd9eah3LDgmCny18WlUSm4OWLDKf Xhf+QpB7YS09O/Zmd50tDGrHU8pdLHiPLcHegYQ7qp1IyOCL7ViEgELQQ EaWUfVPwOP/+SdzUi/zE98aqPHZr3/su/fpaLe8DkQf0zk+2xHcAKKaEH mr+wEerQk0BhZ73QKgKJVOBoz/RpMTgh/vVLKdkXWj0Bj7cYtEnwpxCg9 aLq0cG8X/83TleeL6G1W5Cf2s4TcnEtr2OQr/UEdrJl2qww+biKTM9oPb w86mJI9kbQ7zxRhD+2JUww4LRfUYCdkYxn5x3wAtzcU4XCL30RgSWxkae Q==; X-CSE-ConnectionGUID: Qa6iRv/vRdWaK5v7Wn9L7Q== X-CSE-MsgGUID: x+KvcMFOSx+eokNXtiJmGQ== X-IronPort-AV: E=McAfee;i="6800,10657,11930"; a="345973" X-IronPort-AV: E=Sophos;i="6.27,148,1787036400"; d="scan'208";a="345973" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by orvoesa107.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Oct 2026 06:21:57 -0700 X-CSE-ConnectionGUID: pFKzsu1VQpGp+ICAAy4Q6w== X-CSE-MsgGUID: lNLZKCm2SHCDpb/Q8G4rOQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.27,148,1787036400"; d="scan'208";a="769606" Received: from dut1048pvc.igk.intel.com (HELO localhost) ([10.211.177.228]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Oct 2026 06:21:55 -0700 From: Stephan Rohr To: gdb-patches@sourceware.org Cc: keiths@redhat.com Subject: [PATCH v2 1/1] gdb: fix path expression for anonymous union members Date: Fri, 9 Oct 2026 13:21:39 +0000 Message-ID: <20261009132139.37218-2-stephan.rohr@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261009132139.37218-1-stephan.rohr@intel.com> References: <20261009132139.37218-1-stephan.rohr@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: "Bobko, Sergey" Consider the following structure: struct SBase {}; class S : SBase { private: int a; public: int b; union { int c; }; }; S s; When we create a variable object for variable "s", path expression for the field "s.c" is not computed correctly: interpret mi "-var-info-path-expression var1.public.1_anonymous.public.c" ^done,path_expr="(().c)" To compute a path expression for a struct member, GDB looks up to the parent var object. If the parent has a valid expression, GDB uses it to build the expression for the child. Otherwise, it goes one level up to check the parent->parent, until the valid parent expression is found. The reason for "(().c)" is that the check c_is_path_expr_parent incorrectly returns "true" for the anonymous union parent. Then the path expression of the anonymous union, which is "", is used to compute the result. Inside the check, it takes the type field name by the index of the var-object. The index of the var object is the child index within its parent var-object. However, there are artificial layers for access-levels, e.g., public, private or protected. So the indexing of the child var object might be done within an artificial layer, while the type fields are indexed as a flat list of all fields, including the base types. For the struct above, the var-object tree is as follows: var1 -> index -1 var1.SBase -> index 0 var1.public -> index 1 var1.public.b -> index 0 var1.public.1_anonymous -> index 1 var1.public.1_anonymous.public -> index 0 var1.public.1_anonymous.public.c -> index 0 var1.private -> index 2 var1.private.a -> index 0 Note, that the base type member is the direct child of var1, and the other fields have an artificial access parent. The fields of type S are indexed as: SBase -> 0 a -> 1 b -> 2 "" -> 3 where "" is the anonymous union. Extract the required logic from cplus_describe_child and use it to get the correct index of the field within the parent type. With this change the path expression is computed correctly: interpret mi "-var-info-path-expression var1.public.1_anonymous.public.c" ^done,path_expr="((s).c)" I extended the related test to cover base classes and access-level layers. To compute the expected path expressions, the test library treats a child as a base class if its type equals its name and its parent is not an artificial public/private/protected varobj. Co-Authored-By: Stephan Rohr --- gdb/c-varobj.c | 89 +++++++++++++++++++----------- gdb/testsuite/gdb.mi/mi-var-cp.cc | 22 +++++++- gdb/testsuite/gdb.mi/mi-var-cp.exp | 22 ++++++++ gdb/testsuite/lib/mi-support.exp | 18 +++++- 4 files changed, 118 insertions(+), 33 deletions(-) diff --git a/gdb/c-varobj.c b/gdb/c-varobj.c index 5312eccdaca..dc88ce6e29a 100644 --- a/gdb/c-varobj.c +++ b/gdb/c-varobj.c @@ -22,6 +22,10 @@ static void cplus_class_num_children (struct type *type, int children[3]); +static int +type_field_num_from_child_index (int index, const varobj *parent, + type *parent_type); + /* The names of varobjs representing anonymous structs or unions. */ #define ANONYMOUS_STRUCT_NAME _("") #define ANONYMOUS_UNION_NAME _("") @@ -163,8 +167,12 @@ c_is_path_expr_parent (const struct varobj *var) { const char *field_name; - gdb_assert (var->index < parent_type->num_fields ()); - field_name = parent_type->field (var->index).name (); + int type_index = type_field_num_from_child_index (var->index, + var->parent, + parent_type); + + gdb_assert (type_index < parent_type->num_fields ()); + field_name = parent_type->field (type_index).name (); return !(field_name == NULL || *field_name == '\0'); } } @@ -669,6 +677,51 @@ cplus_name_of_variable (const struct varobj *parent) return c_name_of_variable (parent); } +/* Return the type-field index within the PARENT_TYPE for the child with + the var-index INDEX in the PARENT var object. */ + +static int +type_field_num_from_child_index (int index, const varobj *parent, + type *parent_type) +{ + gdb_assert (parent_type->code () == TYPE_CODE_STRUCT + || parent_type->code () == TYPE_CODE_UNION); + + /* If the parent is not an artificial layer, just return the index. */ + if (!CPLUS_FAKE_CHILD (parent)) + return index; + + /* The fields of the class type are ordered as they + appear in the class. We are given an index for a + particular access control type ("public","protected", + or "private"). We must skip over fields that don't + have the access control we are looking for to properly + find the indexed field. */ + int type_index = TYPE_N_BASECLASSES (parent_type); + int vptr_fieldno; + type *basetype = nullptr; + enum accessibility acc = accessibility::PUBLIC; + + if (parent->name == "private") + acc = accessibility::PRIVATE; + else if (parent->name == "protected") + acc = accessibility::PROTECTED; + + vptr_fieldno = get_vptr_fieldno (parent_type, &basetype); + while (index >= 0) + { + if ((parent_type == basetype && type_index == vptr_fieldno) + || parent_type->field (type_index).is_artificial ()) + ; /* ignore vptr */ + else if (parent_type->field (type_index).accessibility () == acc) + --index; + ++type_index; + } + --type_index; + + return type_index; +} + static void cplus_describe_child (const struct varobj *parent, int index, std::string *cname, struct value **cvalue, struct type **ctype, @@ -710,38 +763,12 @@ cplus_describe_child (const struct varobj *parent, int index, if (CPLUS_FAKE_CHILD (parent)) { - /* The fields of the class type are ordered as they - appear in the class. We are given an index for a - particular access control type ("public","protected", - or "private"). We must skip over fields that don't - have the access control we are looking for to properly - find the indexed field. */ - int type_index = TYPE_N_BASECLASSES (type); - enum accessibility acc = accessibility::PUBLIC; - int vptr_fieldno; - struct type *basetype = NULL; - const char *field_name; - - vptr_fieldno = get_vptr_fieldno (type, &basetype); - if (parent->name == "private") - acc = accessibility::PRIVATE; - else if (parent->name == "protected") - acc = accessibility::PROTECTED; - - while (index >= 0) - { - if ((type == basetype && type_index == vptr_fieldno) - || type->field (type_index).is_artificial ()) - ; /* ignore vptr */ - else if (type->field (type_index).accessibility () == acc) - --index; - ++type_index; - } - --type_index; + int type_index = type_field_num_from_child_index (index, parent, + type); /* If the type is anonymous and the field has no name, set an appropriate name. */ - field_name = type->field (type_index).name (); + const char *field_name = type->field (type_index).name (); if (field_name == NULL || *field_name == '\0') { if (cname) diff --git a/gdb/testsuite/gdb.mi/mi-var-cp.cc b/gdb/testsuite/gdb.mi/mi-var-cp.cc index 624ddce325c..c9326b53c3c 100644 --- a/gdb/testsuite/gdb.mi/mi-var-cp.cc +++ b/gdb/testsuite/gdb.mi/mi-var-cp.cc @@ -205,7 +205,9 @@ int path_expression () /*: END: path_expression :*/ } -class Anonymous +class AnonymousBase {}; +struct SameName { int x; }; +class Anonymous : AnonymousBase { public: struct { /* index: 0 */ @@ -229,8 +231,26 @@ public: int i; char j; }; + struct { + int k; + private: + union { + int l; + char m; + }; + } with_name; }; }; + + /* The vptr is an artificial private field and must not be listed + among the private children. */ + virtual ~Anonymous () {} + +private: + int priv = 9; + /* A member whose name matches its type must not be mistaken for a + base class. */ + struct SameName SameName; }; /* Test anonymous structs and unions. */ diff --git a/gdb/testsuite/gdb.mi/mi-var-cp.exp b/gdb/testsuite/gdb.mi/mi-var-cp.exp index fb850ad51db..84eb7f07b4d 100644 --- a/gdb/testsuite/gdb.mi/mi-var-cp.exp +++ b/gdb/testsuite/gdb.mi/mi-var-cp.exp @@ -56,6 +56,7 @@ mi_execute_to "exec-continue" "breakpoint-hit" \ set tree { Anonymous a { + AnonymousBase AnonymousBase {} {} public { anonymous struct { {} public { @@ -90,11 +91,32 @@ set tree { char j {} } } + "struct {...}" with_name { + {} public { + int k {} + } + {} private { + anonymous union { + {} public { + int l {} + char m {} + } + } + } + } } } } } } + {} private { + int priv {} + SameName SameName { + {} public { + int x {} + } + } + } } } diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp index cd7e0a61189..db8d96dfe99 100644 --- a/gdb/testsuite/lib/mi-support.exp +++ b/gdb/testsuite/lib/mi-support.exp @@ -2353,6 +2353,14 @@ namespace eval ::varobj_tree { return $var_name } + # Check if VARIABLE is one of the CPLUS_FAKE_CHILD varobjs, i.e., it + # doesn't have a type. Such a varobj is one of the artificial + # public/private/protected nodes. + proc is_cplus_fake_child {variable} { + upvar #0 $variable varobj + return [string equal $varobj(type) ""] + } + # Should VARIABLE be used in path expressions? The CPLUS_FAKE_CHILD # varobjs and anonymous structs/unions are not used for path expressions. proc is_path_expr_parent {variable} { @@ -2361,7 +2369,7 @@ namespace eval ::varobj_tree { # If the varobj's type is "", it is a CPLUS_FAKE_CHILD. # If the tail of the varobj's object name is "%d_anonymous", # then it represents an anonymous struct or union. - if {[string length $varobj(type)] == 0 \ + if {[is_cplus_fake_child $variable] \ || [regexp {[0-9]+_anonymous$} $varobj(obj_name)]} { return false } @@ -2374,6 +2382,7 @@ namespace eval ::varobj_tree { proc get_path_expr {parent_variable name type} { upvar #0 $parent_variable parent upvar #0 $parent_variable path_parent + set parent_var_name $parent_variable # If TYPE is "", this is one of the CPLUS_FAKE_CHILD varobjs, # which has no path expression. Likewsise for anonymous structs @@ -2401,6 +2410,13 @@ namespace eval ::varobj_tree { set is_compound 0 } + # A base class has the same name as its type and its parent node is not + # a CPLUS_FAKE_CHILD varobj. Class members are always children of a + # CPLUS_FAKE_CHILD. + if {$type eq $name && ![is_cplus_fake_child $parent_var_name]} { + # This is a base class. + return "((class $type) $path_parent(path_expr))" + } if {[string index $parent(type) end] == "\]"} { # Parent is an array. return "($path_parent(path_expr))\[$name\]" -- 2.43.0 ________________________________________ Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 (89) 99143-0 www.intel.de Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman Chairperson of the Supervisory Board: Sonja Pierer Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.