From patchwork Thu Nov 4 23:08:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 47077 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C365B3857C7C for ; Thu, 4 Nov 2021 23:09:39 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C365B3857C7C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1636067379; bh=2RBJz37wnp7yvJwvV1Jmc4nbuGyjFTaQRpVGiWc4m+c=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=ke3FHy8b9DX4aWhPUllwA+DYjnMx/beDf8kEi5HJlMrnLW3JHL+h4GJCSiKrCj14s /OJ9uo8F/HVD4MGmVspnCwY7Esx5i5OcbCkaxQgSGyooWRoN3zbJbEyIf1qC4BvarZ jWiPSUiYEXEMHOLeqbpSjkR9+BVAXlD2WG/BCqt4= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id DF1E93858036 for ; Thu, 4 Nov 2021 23:08:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DF1E93858036 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-226-gaskLfabNJ6s8zUsmH5WNQ-1; Thu, 04 Nov 2021 19:08:37 -0400 X-MC-Unique: gaskLfabNJ6s8zUsmH5WNQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 70675100C611; Thu, 4 Nov 2021 23:08:36 +0000 (UTC) Received: from localhost (unknown [10.33.36.17]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1A1125D740; Thu, 4 Nov 2021 23:08:35 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Fix pretty printing of std::unique_ptr [PR103086] Date: Thu, 4 Nov 2021 23:08:35 +0000 Message-Id: <20211104230835.2142814-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-14.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Tested x86_64-linux, committed to trunk. Backport to gcc-11 to follow, and maybe to other branches too. Since std::tuple started using [[no_unique_address]] the tuple member of std::unique_ptr has two _M_head_impl subobjects, in different base classes. That means this printer code is ambiguous: tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base head_field = tuple_head_type.fields()[0] if head_field.name == '_M_head_impl': self.pointer = tuple_member['_M_head_impl'] In older versions of GDB it happened to work by chance, because GDB returned the last _M_head_impl member and std::tuple's base classes are stored in reverse order, so the last one was the T* element of the tuple. Since GDB 11 it returns the first _M_head_impl, which is the deleter element. The fix is for the printer to stop using an ambiguous field name and cast the tuple to the correct base class before accessing the _M_head_impl member. Instead of fixing this in both UniquePointerPrinter and StdPathPrinter a new unique_ptr_get function is defined to do it correctly. That is defined in terms of new tuple_get and _tuple_impl_get functions. It would be possible to reuse _tuple_impl_get to access each element in StdTuplePrinter._iterator.__next__, but that already does the correct casting, and wouldn't be much simpler anyway. libstdc++-v3/ChangeLog: PR libstdc++/103086 * python/libstdcxx/v6/printers.py (_tuple_impl_get): New helper for accessing the tuple element stored in a _Tuple_impl node. (tuple_get): New function for accessing a tuple element. (unique_ptr_get): New function for accessing a unique_ptr. (UniquePointerPrinter, StdPathPrinter): Use unique_ptr_get. * python/libstdcxx/v6/xmethods.py (UniquePtrGetWorker): Cast tuple to its base class before accessing _M_head_impl. --- libstdc++-v3/python/libstdcxx/v6/printers.py | 71 ++++++++++++++------ libstdc++-v3/python/libstdcxx/v6/xmethods.py | 2 +- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index c7da4079a7d..c5072c52281 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -240,32 +240,63 @@ class SharedPointerPrinter: state = 'use count %d, weak count %d' % (usecount, weakcount - 1) return '%s<%s> (%s)' % (self.typename, str(self.val.type.template_argument(0)), state) +def _tuple_impl_get(val): + "Return the tuple element stored in a _Tuple_impl base class." + bases = val.type.fields() + if not bases[-1].is_base_class: + raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type)) + # Get the _Head_base base class: + head_base = val.cast(bases[-1].type) + fields = head_base.type.fields() + if len(fields) == 0: + raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type)) + if fields[0].name == '_M_head_impl': + # The tuple element is the _Head_base::_M_head_impl data member. + return head_base['_M_head_impl'] + elif fields[0].is_base_class: + # The tuple element is an empty base class of _Head_base. + # Cast to that empty base class. + return head_base.cast(fields[0].type) + else: + raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type)) + +def tuple_get(n, val): + "Return the result of std::get(val) on a std::tuple" + tuple_size = len(get_template_arg_list(val.type)) + if n > tuple_size: + raise ValueError("Out of range index for std::get on std::tuple") + # Get the first _Tuple_impl<0, T...> base class: + node = val.cast(val.type.fields()[0].type) + while n > 0: + # Descend through the base classes until the Nth one. + node = node.cast(node.type.fields()[0].type) + n -= 1 + return _tuple_impl_get(node) + +def unique_ptr_get(val): + "Return the result of val.get() on a std::unique_ptr" + # std::unique_ptr contains a std::tuple, + # either as a direct data member _M_t (the old implementation) + # or within a data member of type __uniq_ptr_data. + impl_type = val.type.fields()[0].type.strip_typedefs() + # Check for new implementations first: + if is_specialization_of(impl_type, '__uniq_ptr_data') \ + or is_specialization_of(impl_type, '__uniq_ptr_impl'): + tuple_member = val['_M_t']['_M_t'] + elif is_specialization_of(impl_type, 'tuple'): + tuple_member = val['_M_t'] + else: + raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type)) + return tuple_get(0, tuple_member) + class UniquePointerPrinter: "Print a unique_ptr" def __init__ (self, typename, val): self.val = val - impl_type = val.type.fields()[0].type.strip_typedefs() - # Check for new implementations first: - if is_specialization_of(impl_type, '__uniq_ptr_data') \ - or is_specialization_of(impl_type, '__uniq_ptr_impl'): - tuple_member = val['_M_t']['_M_t'] - elif is_specialization_of(impl_type, 'tuple'): - tuple_member = val['_M_t'] - else: - raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type)) - tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl - tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base - head_field = tuple_head_type.fields()[0] - if head_field.name == '_M_head_impl': - self.pointer = tuple_member['_M_head_impl'] - elif head_field.is_base_class: - self.pointer = tuple_member.cast(head_field.type) - else: - raise ValueError("Unsupported implementation for tuple in unique_ptr: %s" % str(impl_type)) def children (self): - return SmartPtrIterator(self.pointer) + return SmartPtrIterator(unique_ptr_get(self.val)) def to_string (self): return ('std::unique_ptr<%s>' % (str(self.val.type.template_argument(0)))) @@ -1370,7 +1401,7 @@ class StdPathPrinter: def __init__ (self, typename, val): self.val = val self.typename = typename - impl = self.val['_M_cmpts']['_M_impl']['_M_t']['_M_t']['_M_head_impl'] + impl = unique_ptr_get(self.val['_M_cmpts']['_M_impl']) self.type = impl.cast(gdb.lookup_type('uintptr_t')) & 3 if self.type == 0: self.impl = impl diff --git a/libstdc++-v3/python/libstdcxx/v6/xmethods.py b/libstdc++-v3/python/libstdcxx/v6/xmethods.py index 77870e9a264..991d945321a 100644 --- a/libstdc++-v3/python/libstdcxx/v6/xmethods.py +++ b/libstdc++-v3/python/libstdcxx/v6/xmethods.py @@ -597,7 +597,7 @@ class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base head_field = tuple_head_type.fields()[0] if head_field.name == '_M_head_impl': - return tuple_member['_M_head_impl'] + return tuple_member.cast(tuple_head_type)['_M_head_impl'] elif head_field.is_base_class: return tuple_member.cast(head_field.type) else: