From patchwork Fri Mar 20 13:05:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 132038 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 79E1B4C318BB for ; Fri, 20 Mar 2026 13:06:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 79E1B4C318BB Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=adacore.com header.i=@adacore.com header.a=rsa-sha256 header.s=google header.b=SO6hVlX/ X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-oa1-x2b.google.com (mail-oa1-x2b.google.com [IPv6:2001:4860:4864:20::2b]) by sourceware.org (Postfix) with ESMTPS id 6068F4BAE7FD for ; Fri, 20 Mar 2026 13:05:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6068F4BAE7FD Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 6068F4BAE7FD Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2001:4860:4864:20::2b ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774011956; cv=none; b=ObCIGUhO1Qj8pfAExQQ84VZImeLqSnuBEsfRU14jfq2Z+LUA4tbBpNH7MxMGxMr/8uxQEPAyVRgTkS5VvflgYvyapFe3He67RWWFvS3QktUpZksBemlsuCZL2K0MjuP5AyWAmX0EYxjy8JJvWBWbfiiP2ZA17lUlK6PTY1O5ByE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774011956; c=relaxed/simple; bh=MhbMXLQoL0L9TOrB20D903FPTJ+OuyvBV/gNkid09Xw=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=l47RaVCgkeEqKRztWmHAxEjvx/C3kNeQZSMEQskpG0ItqUBlHjK5RvoEh6KECg6v6NpmdkXPgeZ5Z3l07WX70y39FKh5/ATXXpvwYKFo2k1rIwwFbOVuu2o1K+grylHQQIXIOI5cwk+pC2ui3eeJeVMA7wnIfC/dE4HxkCTD3Tw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6068F4BAE7FD Received: by mail-oa1-x2b.google.com with SMTP id 586e51a60fabf-415b23dd6e5so224488fac.3 for ; Fri, 20 Mar 2026 06:05:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; t=1774011955; x=1774616755; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=xrc6L156Nq957uVir2hPLNJqL4PtrtjKSmmFFUBDq3k=; b=SO6hVlX/sjM8aE7n6fMDZ8+DpEibv6kF9pTa1dOZm7rITCSJN7aqMK5zCXDc2AFDkF lPij92MO46fcEu8FMggS6TsPedZGCb/UDxmlAghxgAt8FnOXp4ohj3mEFXYHrFSg7Mqd RAYZg8ahT9jMe08SBbvRhjXX2Sq4HtELQ1LMYM3shywvGptoMj8Bffs6EGmKv1lxXEn2 CwcD8AZDkAj4JbUaXw8FYeZbe6j9FOD71ZOJUI06ZwKQh9fWPUTmEqWYIsk13AOrEqlD 9r7G64zTi1UV4seAhzGBe9qQJhZ3Y+/0hLjcjjl3hpBPI410yK3vIf0fQ7VT/asagQSY 2UGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774011955; x=1774616755; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=xrc6L156Nq957uVir2hPLNJqL4PtrtjKSmmFFUBDq3k=; b=X0Dfm1pjVtWNg0kayOLIUGuCtpANv7GVhttVFrDnG080jTr3lqzVUVCotaZCoRSCEp 8cthejfHR21TdUqHf3SYq+HbU0iINi3B67is+q4nXX9s+ijlAShBS6txs8Sq9zaci75u gxZPvsnTXOhugdUxiB4qwqOMk7pmmor+aShQ5atM4mqGJAQNGeUsMMue+bhmpZZ2QpDQ xyb5wbyUgqRytv9BmNjNJX7G5YImyHTwzT/wHswVYLl73Q4ybkfujwiHnG/XZPHwrk+P qrATPwgBgDZTr+D8kdDVoBTacXLaAOaE3En8SLu+I3jbkMVCBUY9XAhDI28vBPl00OrC tuww== X-Gm-Message-State: AOJu0YxUswUnwX3SDQ63NCSd6s2sBqDmoB7/BOdsPByq5quWMjQKNKGy bze83eIpx4hcSX5I2BF5kFdNJ71pCriStaYaiXiD97mfNSiDsQPKZF/nTrQYnXfW7GFcoEJuDlw x1ko= X-Gm-Gg: ATEYQzwuFhkSdCnoK8c53Vb2siR2Z859wscXn1UQs92umIuPegjBkycJM05y8Eih4Ix dgYqQEpaGk6um6sMxpS4vpBFC7qMdlvE2amoTHlMSa5OvCo6jPtiIONMCB5PKmg8EG8qyUO7nZv UiYXqzr7fLbXjkfDgdOzcQt/XF+D1pyxf/ZGyQG+A70T/o0/ZlrmOiF+gSWhJDU/Ddu0ddXnv2F 4xxMdxbGH4GAqXKX1zxqexnIOfzaBtZ9sSyQNeGhWU0stOuV7k24+6NT+Kh/IpTiUGFLb2+tFM+ QEuMSuKLE4MmGyiBq9/laS1CkeYUBztPxHNmLZ8RCr5csTRbV4yBRXkAfeUfexntN51gj2ucF5H UEhHH4+GhGPWp4O4iDVYB+zBjwy4NXvFSuCJ3i+mttN4Si2hkEjuTXNM5d/q+ttViEOSQ9PeZ1g jjxMK6gpSqvrdoJDAj6znOjTYoM6x7TYdU3Pd6adbUeG8= X-Received: by 2002:a05:6870:63ab:b0:417:6224:5855 with SMTP id 586e51a60fabf-41c10c6ccaemr1786603fac.0.1774011954793; Fri, 20 Mar 2026 06:05:54 -0700 (PDT) Received: from bapiya (75-166-225-82.hlrn.qwest.net. [75.166.225.82]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-41c14d73a19sm1848435fac.11.2026.03.20.06.05.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Mar 2026 06:05:54 -0700 (PDT) From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [PATCH] Handle inherited discriminants in Ada Date: Fri, 20 Mar 2026 07:05:51 -0600 Message-ID: <20260320130551.1320760-1-tromey@adacore.com> X-Mailer: git-send-email 2.53.0 MIME-Version: 1.0 X-Spam-Status: No, score=-11.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org In Ada, a discriminant might be inherited. Consider this code: type Root_T (Root_Disc : Boolean) is tagged record null; end record; type Child_T (Child_Disc : Boolean) is new Root_T (Root_Disc => Child_Disc) with record case Child_Disc is when True => Child_Flag : Boolean; when others => null; end case; end record; Here, Child_Disc does not really exist -- it just an alias of Root_Disc. Now, DWARF doesn't recognize this possibility, so compilers have come up with two different approaches to handle this. gnat-llvm will emit an artificial copy of Root_Disc as a member of Child_T. See commit 48b5669c, where this was handled in gdb. It wasn't convenient to follow this same approach in GCC (the two compilers have very different DWARF generation approaches), and so GCC emits the possibly-more-intuitive approach of simply having the DW_AT_discr refer to the field DIE in Root_T. This patch implements support for this approach in gdb. The idea here is that, rather than try to figure out how to handle cross-type references, gdb will implement the "LLVM" approach internally; that is, make an artificial duplicate field. --- gdb/dwarf2/read.c | 60 +++++-- gdb/testsuite/gdb.dwarf2/inherited-variant.c | 23 +++ .../gdb.dwarf2/inherited-variant.exp | 149 ++++++++++++++++++ 3 files changed, 222 insertions(+), 10 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/inherited-variant.c create mode 100644 gdb/testsuite/gdb.dwarf2/inherited-variant.exp base-commit: e5425f2687d66034a8d3fe94264cf99b42c1cb1a diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 8b87d58dd9c..0ab5ad984ea 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -636,6 +636,14 @@ struct fnfieldlist in an instance of a field_info structure, as defined below. */ struct field_info { + explicit field_info (die_info *base) + : base_die (base) + { + } + + /* The DIE for the overall structure. */ + die_info *base_die; + /* List of data member and baseclasses fields. */ std::vector fields; std::vector baseclasses; @@ -811,9 +819,6 @@ static void get_scope_pc_bounds (struct die_info *, static void dwarf2_record_block_ranges (struct die_info *, struct block *, struct dwarf2_cu *); -static void dwarf2_add_field (struct field_info *, struct die_info *, - struct dwarf2_cu *); - static void dwarf2_attach_fields_to_type (struct field_info *, struct type *, struct dwarf2_cu *); @@ -9485,7 +9490,7 @@ compute_field_location (dwarf2_cu *cu, die_info *die, field *fp) static void dwarf2_add_field (struct field_info *fip, struct die_info *die, - struct dwarf2_cu *cu) + struct dwarf2_cu *cu, bool force_artificial = false) { struct nextfield *new_field; struct attribute *attr; @@ -9519,6 +9524,9 @@ dwarf2_add_field (struct field_info *fip, struct die_info *die, fp = &new_field->field; + if (force_artificial) + fp->set_is_artificial (true); + if ((die->tag == DW_TAG_member || die->tag == DW_TAG_namelist_item) && !die_is_declaration (die, cu)) { @@ -10559,6 +10567,29 @@ read_structure_type (struct die_info *die, struct dwarf2_cu *cu) return type; } +/* Return true if DIE appears to be nested in the structure being + defined by FI. */ + +static bool +field_info_encloses_die (field_info *fi, die_info *die) +{ + for (; die != nullptr; die = die->parent) + { + if (die == fi->base_die) + return true; + + /* If the current DIE is not a member, then maybe we found a DIE + that is nested in some other object that is itself nested in + the outermost structure. We do allow nesting in variants + (though it's unclear if this really makes sense). */ + if (die->tag != DW_TAG_member && die->tag != DW_TAG_variant + && die->tag != DW_TAG_variant_part) + return false; + } + + return false; +} + static void handle_struct_member_die (struct die_info *child_die, struct type *type, @@ -10592,11 +10623,6 @@ handle_variant_part (struct die_info *die, struct type *type, new_part = ¤t.variant_parts.emplace_back (); } - /* When we recurse, we want callees to add to this new variant - part. */ - scoped_restore save_current_variant_part - = make_scoped_restore (&fi->current_variant_part, new_part); - struct attribute *discr = dwarf2_attr (die, DW_AT_discr, cu); if (discr == NULL) { @@ -10608,6 +10634,15 @@ handle_variant_part (struct die_info *die, struct type *type, struct die_info *target_die = follow_die_ref (die, discr, &target_cu); new_part->discriminant_offset = target_die->sect_off; + + /* In Ada, a discriminant might be inherited from some + superclass. DWARF does not admit this possibility, so + compilers have adapted in one of two ways: LLVM emits a local + copy of the field (marking it as artificial); but GCC just + references the field DIE in the parent type. Here we handle + the GCC case by creating an artificial copy of the field. */ + if (!field_info_encloses_die (fi, target_die)) + dwarf2_add_field (fi, target_die, cu, true); } else { @@ -10617,6 +10652,10 @@ handle_variant_part (struct die_info *die, struct type *type, objfile_name (cu->per_objfile->objfile)); } + /* When we recurse, we want callees to add to this new variant + part. */ + scoped_restore save_current_variant_part + = make_scoped_restore (&fi->current_variant_part, new_part); for (die_info *child_die : die->children ()) handle_struct_member_die (child_die, type, fi, template_args, cu); } @@ -10763,7 +10802,8 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) bool has_template_parameters = false; if (die->child != NULL && ! die_is_declaration (die, cu)) { - struct field_info fi; + field_info fi (die); + std::vector template_args; for (die_info *child_die : die->children ()) diff --git a/gdb/testsuite/gdb.dwarf2/inherited-variant.c b/gdb/testsuite/gdb.dwarf2/inherited-variant.c new file mode 100644 index 00000000000..9d70b82553c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/inherited-variant.c @@ -0,0 +1,23 @@ +/* Copyright 2026 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 . */ + +unsigned char bufy[2] = { 1, 1 }; +unsigned char bufn[2] = { 0, 0 }; + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/inherited-variant.exp b/gdb/testsuite/gdb.dwarf2/inherited-variant.exp new file mode 100644 index 00000000000..5874f0f3e6e --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/inherited-variant.exp @@ -0,0 +1,149 @@ +# Copyright 2026 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 an inherited discriminant. +# +# In Ada, you can have a type with variant parts where the +# discriminant is inherited from a superclass. For instance this can +# look like: +# +# type Root_T (Root_Disc : Boolean) is tagged record +# Name : String (1 .. 8); +# Flag_1 : Boolean; +# Flag_2 : Boolean; +# end record; +# type Child_T (Child_Disc : Boolean) is new Root_T (Root_Disc => Child_Disc) with record +# case Child_Disc is +# when True => +# Child_Flag : Boolean; +# when others => null; +# end case; +# end record; +# +# Here, Child_Disc does not really exist -- it is just an alias for +# Root_Disc. So, the DW_TAG_variant_part in Child_T will have a +# DW_AT_discr referring to... what? +# +# Note that DWARF does not consider this problem, so there is no +# standard solution. LLVM handles this by emitting a copy of the +# Root_Disc field, but marked as artificial; this way gdb's Ada +# code knows not to print it. GCC takes a different approach, +# namely having the DW_AT_discr refer to the original Root_Disc +# DIE, that appears in the Root_T structure. +# +# This test checks the latter scenario. The type is slightly +# different than the above, just to make it simpler to write this +# test. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use +# gas. +require dwarf2_support + +standard_testfile .c .S + +# Make some DWARF for the test. +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + upvar cu_lang cu_lang + + declare_labels bool_label root_label root_disc child_label + + cu { addr_size 4 } { + compile_unit { + DW_AT_name user.adb + DW_AT_language @DW_LANG_Ada95 + } { + bool_label: DW_TAG_base_type { + DW_AT_byte_size 1 DW_FORM_sdata + DW_AT_encoding @DW_ATE_boolean + DW_AT_name bool + } + + root_label: structure_type { + DW_AT_name root + DW_AT_byte_size 1 DW_FORM_sdata + } { + root_disc: DW_TAG_member { + DW_AT_name root_disc + DW_AT_type :$bool_label + DW_AT_data_member_location 0 data1 + } + } + + child_label: DW_TAG_structure_type { + DW_AT_name child + DW_AT_byte_size 2 DW_FORM_sdata + } { + DW_TAG_member { + DW_AT_name _parent + DW_AT_type :$root_label + DW_AT_data_member_location 0 DW_FORM_data1 + } + + DW_TAG_variant_part { + DW_AT_discr :$root_disc DW_FORM_ref4 + } { + DW_TAG_variant { + DW_AT_discr_value 1 DW_FORM_udata + } { + DW_TAG_member { + DW_AT_name child_flag DW_FORM_strp + DW_AT_type :$bool_label + DW_AT_data_member_location 1 DW_FORM_data1 + } + } + } + } + + DW_TAG_variable { + DW_AT_name "withchild" + DW_AT_type :$child_label + DW_AT_external 1 DW_FORM_flag + DW_AT_location { + DW_OP_addr [gdb_target_symbol "bufy"] + } SPECIAL_expr + } + + DW_TAG_variable { + DW_AT_name "withoutchild" + DW_AT_type :$child_label + DW_AT_external 1 DW_FORM_flag + DW_AT_location { + DW_OP_addr [gdb_target_symbol "bufn"] + } SPECIAL_expr + } + } + } +} + +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] debug]} { + return +} + +if {![runto_main]} { + return +} + +gdb_test "set language ada" + +gdb_test "print withchild" \ + [quotemeta {$@DECIMAL = (root_disc => true, child_flag => true)}] +gdb_test "print withchild.child_flag" \ + [quotemeta {$@DECIMAL = true}] +gdb_test "print withoutchild" \ + [quotemeta {$@DECIMAL = (root_disc => false)}]