Bug 28480 - Incorrect field ambiguity detection due to [[no_unique_address]]
Summary: Incorrect field ambiguity detection due to [[no_unique_address]]
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: c++ (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-10-20 22:43 UTC by Jonathan Wakely
Modified: 2021-12-11 07:49 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2021-10-26 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Wakely 2021-10-20 22:43:13 UTC
On fedora, with libstdc++-debuginfo installed, and either rawhide's gdb-11 or a self-built gdb-12 from git.

#include <memory>
#include <string>

struct datum
{
  std::string s;
  int i;
};

int
main()
{
  std::unique_ptr<datum> uptr (new datum);
  uptr->s = "hi bob";
  uptr->i = 23;
  std::unique_ptr<datum> &ruptr = uptr;
}

$ g++ -g cxx11.cc
$ gdb ./a.out
GNU gdb (GDB) Fedora 11.1-2.fc36
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(gdb) start
Temporary breakpoint 1 at 0x40118f: file cxx11.cc, line 13.
Starting program: /tmp/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Temporary breakpoint 1, main () at cxx11.cc:13
13        std::unique_ptr<datum> uptr (new datum);
(gdb) n
14        uptr->s = "hi bob";
(gdb) 
15        uptr->i = 23;
(gdb) 
16        std::unique_ptr<datum> &ruptr = uptr;
(gdb) p uptr
$1 = std::unique_ptr<datum> = {get() = {<No data fields>}}


This "<No data fields>" is wrong, it's a datum* pointer, and should be printed as such.

This works with GDB 10, but not GDB 11 in rawhide or a self-built GDB 12 from git.
Comment 1 Jonathan Wakely 2021-10-21 00:44:48 UTC
87a37e5e078f506fa9905b74e9238593c537fcd5 is the first bad commit
commit 87a37e5e078f506fa9905b74e9238593c537fcd5
Author: Pedro Alves 
Date:   Fri Aug 28 21:10:59 2020 +0100

    Reject ambiguous C++ field accesses (PR exp/26602)
    
    The gdb.cp/ambiguous.exp testcase had been disabled for many years,
    but recently it was re-enabled.  However, it is failing everywhere.
    That is because it is testing an old feature that is gone from GDB.
Comment 2 Jonathan Wakely 2021-10-21 00:49:13 UTC
$ valgrind /tmp/gdb/gdb/gdb -q -ex 'br 16' -ex r -ex 'p uptr' -ex qq /tmp/a.out  | grep 'get() ='
==1770130== Memcheck, a memory error detector
==1770130== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1770130== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==1770130== Command: /tmp/gdb/gdb/gdb -q -ex br\ 16 -ex r -ex p\ uptr -ex qq /tmp/a.out
==1770130== 
warning: linux_ptrace_test_ret_to_nx: WSTOPSIG 6 is neither SIGTRAP nor SIGSEGV!
==1770130== Invalid read of size 1
==1770130==    at 0x484A2F0: memmove (vg_replace_strmem.c:1382)
==1770130==    by 0x76FE49: memcpy (string_fortified.h:29)
==1770130==    by 0x76FE49: value_contents_copy_raw(value*, long, value*, long, long) (value.c:1330)
==1770130==    by 0x7735C0: value_primitive_field(value*, long, int, type*) (value.c:3036)
==1770130==    by 0x764152: struct_field_searcher::search(value*, long, type*) (valops.c:1901)
==1770130==    by 0x763E40: struct_field_searcher::search(value*, long, type*) (valops.c:2002)
==1770130==    by 0x763E40: struct_field_searcher::search(value*, long, type*) (valops.c:2002)
==1770130==    by 0x763E40: struct_field_searcher::search(value*, long, type*) (valops.c:2002)
==1770130==    by 0x76423D: search_struct_field(char const*, value*, type*, int) (valops.c:2023)
==1770130==    by 0x766CF0: value_struct_elt(value**, value**, char const*, int*, char const*) (valops.c:2251)
==1770130==    by 0x67DDB6: valpy_getitem(_object*, _object*) (py-value.c:990)
==1770130==    by 0x497D836: _PyEval_EvalFrameDefault (ceval.c:2101)
==1770130==    by 0x497BE93: UnknownInlinedFun (pycore_ceval.h:46)
==1770130==    by 0x497BE93: _PyEval_Vector (ceval.c:5073)
==1770130==  Address 0x536a99f is 1 bytes before a block of size 8 alloc'd
==1770130==    at 0x4845464: calloc (vg_replace_malloc.c:1328)
==1770130==    by 0x456D60: xcalloc (alloc.c:100)
==1770130==    by 0x770986: allocate_value_contents (value.c:1022)
==1770130==    by 0x770986: allocate_value_contents (value.c:1016)
==1770130==    by 0x770986: allocate_value(type*) (value.c:1033)
==1770130==    by 0x77359B: value_primitive_field(value*, long, int, type*) (value.c:3035)
==1770130==    by 0x764152: struct_field_searcher::search(value*, long, type*) (valops.c:1901)
==1770130==    by 0x763E40: struct_field_searcher::search(value*, long, type*) (valops.c:2002)
==1770130==    by 0x76423D: search_struct_field(char const*, value*, type*, int) (valops.c:2023)
==1770130==    by 0x766CF0: value_struct_elt(value**, value**, char const*, int*, char const*) (valops.c:2251)
==1770130==    by 0x67DDB6: valpy_getitem(_object*, _object*) (py-value.c:990)
==1770130==    by 0x497D836: _PyEval_EvalFrameDefault (ceval.c:2101)
==1770130==    by 0x497BE93: UnknownInlinedFun (pycore_ceval.h:46)
==1770130==    by 0x497BE93: _PyEval_Vector (ceval.c:5073)
==1770130==    by 0x4985890: UnknownInlinedFun (call.c:342)
==1770130==    by 0x4985890: _PyObject_FastCallDictTstate (call.c:142)
==1770130== 
$1 = std::unique_ptr<datum> = {get() = {<No data fields>}}
==1770130== 
==1770130== HEAP SUMMARY:
==1770130==     in use at exit: 18,182,520 bytes in 34,241 blocks
==1770130==   total heap usage: 259,945 allocs, 225,704 frees, 171,556,058 bytes allocated
==1770130== 
==1770130== LEAK SUMMARY:
==1770130==    definitely lost: 160 bytes in 3 blocks
==1770130==    indirectly lost: 0 bytes in 0 blocks
==1770130==      possibly lost: 446,388 bytes in 3,992 blocks
==1770130==    still reachable: 17,735,972 bytes in 30,246 blocks
==1770130==         suppressed: 0 bytes in 0 blocks
==1770130== Rerun with --leak-check=full to see details of leaked memory
==1770130== 
==1770130== For lists of detected and suppressed errors, rerun with: -s
==1770130== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Comment 3 Jonathan Wakely 2021-10-21 00:56:24 UTC
The valgrind error is already present before the commit that introduced the bug, so is probably unrelated.
Comment 4 Simon Marchi 2021-10-21 03:23:44 UTC
Ah, I do get this (the memory error, but from ASan) when debugging GDB, very annoying.  I think I dug a little bit at some point to try to understand, but I forgot.  I'll give it another try.
Comment 5 Jonathan Wakely 2021-10-21 08:44:07 UTC
Well as I said, the memory error was present at the previous commit too, so seems unrelated to the pretty printing regression (maybe it should be a separate bug though).
Comment 6 Guinevere Larsen 2021-10-26 19:14:30 UTC
I'm starting to look into this, and I can confirm that the bug is there and that Wakely's commit is the one that introduced the problem.
Comment 7 Guinevere Larsen 2021-11-04 16:01:15 UTC
After looking at this for a bit, I have an RFC. My fix changes the order in which Pedro's searcher returns things. I'm not sure this is the best way to handle a pretty printer bug, but it's a starting point. feedback welcome: https://sourceware.org/pipermail/gdb-patches/2021-November/183084.html
Comment 8 Jonathan Wakely 2021-11-04 16:52:52 UTC
Standalone reproducer:

namespace std
{
#if __has_cpp_attribute(__no_unique_address__)
#warning using attr
  template<typename T>
    struct _Head_base
    {
      [[__no_unique_address__]] T _M_head_impl;
    };
#else
#warning not using attr
  template<typename T>
    struct _Head_base : T
    { };
#endif

  template<typename T>
    struct _Head_base<T*>
    {
      T* _M_head_impl;
    };

  template<unsigned long, typename ...> struct _Tuple_impl;

  template<typename T, typename U>
    struct _Tuple_impl<0, T, U> : _Tuple_impl<1, U>, _Head_base<T>
    { };

  template<typename U>
    struct _Tuple_impl<1, U> : _Head_base<U>
    { };

  template<typename T, typename U>
    struct tuple : _Tuple_impl<0, T, U>
    { };

  template<typename T> struct default_delete { };

  template<typename T, typename D = default_delete<T>>
    struct unique_ptr
    {
      unique_ptr(T* p) { _M_t._Head_base<T*>::_M_head_impl = p; }

      using __tuple_type = tuple<T*, D>;

      __tuple_type _M_t;
    };
}

struct datum
{
  const char* s;
  int i;
};

int
main()
{
  std::unique_ptr<datum> uptr (new datum);
  return 0;
}

compiled with GCC 9 or above this uses the [[no_unique_address]] attribute, an GDB gets confused:

$ GCC 8 up.C -Wno-unused && ~/gcc/gdb/11/bin/gdb -q  -ex 'start' -ex n -ex  'p uptr' a.out
up.C:11:2: warning: #warning not using attr [-Wcpp]
 #warning not using attr
  ^~~~~~~
Reading symbols from a.out...
Temporary breakpoint 1 at 0x40112a: file up.C, line 59.
Starting program: /tmp/a.out 

Temporary breakpoint 1, main () at up.C:59
59        std::unique_ptr<datum> uptr (new datum);
60        return 0;
$1 = std::unique_ptr<datum> = {get() = 0x416eb0}


$ GCC 9 up.C -Wno-unused && ~/gcc/gdb/11/bin/gdb -q  -ex 'start' -ex n -ex  'p uptr' a.out
up.C:4:2: warning: #warning using attr [-Wcpp]
    4 | #warning using attr
      |  ^~~~~~~
Reading symbols from a.out...
Temporary breakpoint 1 at 0x40112a: file up.C, line 59.
Starting program: /tmp/a.out 

Temporary breakpoint 1, main () at up.C:59
59        std::unique_ptr<datum> uptr (new datum);
60        return 0;
$1 = std::unique_ptr<datum> = {get() = {<No data fields>}}
Comment 9 Jonathan Wakely 2021-11-04 17:06:19 UTC
It's a bug in the libstdc++ printers:

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']  ## BUG
        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))


On the line marked BUG we get different behaviour depending on whether the class uses [[no_unique_address]] or not. With the [[no_unique_address]] attribute there are two subobjects called _M_head_impl and so the printer is wrong to use tuple_member['_M_head_impl'] and assume that the right one gets chosen.

When the [[no_unique_address]] attribute is not used (i.e. GCC 8 and older) there is only one mnember called _M_head_impl and it works OK.

Pedro's patch must have changed which member gets used by the ['_M_head_impl'] member access. And that's fine -- it's ambiguous, so the printer is wrong to rely on a particular one being returned.

I will fix the printer ...
Comment 10 Jonathan Wakely 2021-11-04 17:09:26 UTC
I think this is the fix:

--- a/libstdc++-v3/python/libstdcxx/v6/printers.py
+++ b/libstdc++-v3/python/libstdcxx/v6/printers.py
@@ -258,7 +258,7 @@ class UniquePointerPrinter:
         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']
+            self.pointer = tuple_member.cast(tuple_head_type)['_M_head_impl']
         elif head_field.is_base_class:
             self.pointer = tuple_member.cast(head_field.type)
         else:
Comment 11 Jonathan Wakely 2021-11-04 17:36:31 UTC
I've created https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103086 for this, with a more detailed explanation.

I think GDB's new behaviour is probably better, and the libstdc++ printers should be fixed.

However, Bruno did suggest that when tuple_member['_M_head_impl'] is ambiguous it should give an error, not silently return either the first or last matching member.

For example, in this code:

struct X
{
  int i = 0x1;
};

struct Y
{
  char i = 'Y';
};

struct Z : X, Y { };

int main()
{
  Z z;
  return 0;
}

GDB 10 will happily print z.i

$ gdb -ex start -ex n -ex "py print(gdb.parse_and_eval('z')['i'])"  a.out
GNU gdb (GDB) Fedora 10.2-3.fc34
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...
Temporary breakpoint 1 at 0x40110a: file ambig.cc, line 15.
Starting program: /tmp/a.out 

Temporary breakpoint 1, main () at ambig.cc:15
15        Z z;
16        return 0;
89 'Y'

But GDB 12 gives an error:

$ ~/gcc/gdb/11/bin/gdb -ex start -ex n -ex "py print(gdb.parse_and_eval('z')['i'])"  a.out
GNU gdb (GDB) 12.0.50.20211020-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...
Temporary breakpoint 1 at 0x40110a: file ambig.cc, line 15.
Starting program: /tmp/a.out 

Temporary breakpoint 1, main () at ambig.cc:15
15        Z z;
16        return 0;
Traceback (most recent call last):
  File "<string>", line 1, in <module>
gdb.error: Request for member 'i' is ambiguous in type 'Z'. Candidates are:
  'int X::i' (Z -> X)
  'char Y::i' (Z -> Y)
Error while executing Python code.

I wonder why there wasn't an error for tuple_member['_M_head_impl'] in the libstdc++ printer.
Comment 12 Jonathan Wakely 2021-11-04 17:42:51 UTC
Aha, the std::tuple case is more like this:

struct X
{
  int i = 0x1;
};

struct Y
{
  struct S { };
  [[no_unique_address]] S i;
};

struct XX : X { };

struct Z : Y, XX { };

int main()
{
  Z z;
  return 0;
}

Here z.i is ambiguous, but GDB doesn't diagnose it.

And GDB 10 returns z.X::i but GDB 11 returns z.Y::i
Comment 13 Jonathan Wakely 2021-11-04 17:53:53 UTC
According to the C++ standard, z.i finds i in two base classes, so it's ambiguous.

GDB seems to consider it non-ambiguous, maybe because they're at different depths?  i.e. we have z.Y::i and z.XX::X::i where Y::i is in a direct base class, and X::i is in a base class of a base class. But C++ says that is still ambiguous, because X::i is also accessible as z.XX::i and that's ambiguous with z.Y::i
Comment 14 Simon Marchi 2021-11-04 17:56:57 UTC
(In reply to Jonathan Wakely from comment #12)
> Aha, the std::tuple case is more like this:
> 
> struct X
> {
>   int i = 0x1;
> };
> 
> struct Y
> {
>   struct S { };
>   [[no_unique_address]] S i;
> };
> 
> struct XX : X { };
> 
> struct Z : Y, XX { };
> 
> int main()
> {
>   Z z;
>   return 0;
> }

I think you can get rid of XX (make Z directly inherit from X) and it still reproduces.

> 
> Here z.i is ambiguous, but GDB doesn't diagnose it.
> 
> And GDB 10 returns z.X::i but GDB 11 returns z.Y::i

If I try to use z.i in the program, it doesn't compile:

test.cpp: In function ‘int main()’:
test.cpp:17:12: error: request for member ‘i’ is ambiguous
   17 |   return z.i;
      |            ^
test.cpp:3:7: note: candidates are: ‘int X::i’
    3 |   int i = 0x1;
      |       ^
test.cpp:9:27: note:                 ‘Y::S Y::i’
    9 |   [[no_unique_address]] S i;
      |                           ^

So I agree, it makes sense for GDB to not accept the expression.  It looks like the intent is to reject and (and print the "request is ambiguous" message), but that the no_unique_address tag confuses GDB.
Comment 15 Simon Marchi 2021-11-04 17:59:03 UTC
(In reply to Jonathan Wakely from comment #13)
> According to the C++ standard, z.i finds i in two base classes, so it's
> ambiguous.
> 
> GDB seems to consider it non-ambiguous, maybe because they're at different
> depths?  i.e. we have z.Y::i and z.XX::X::i where Y::i is in a direct base
> class, and X::i is in a base class of a base class. But C++ says that is
> still ambiguous, because X::i is also accessible as z.XX::i and that's
> ambiguous with z.Y::i

I don't think it's the depth that confuses GDB, it seems to be the no_unique_address.
Comment 16 Guinevere Larsen 2021-11-04 18:07:46 UTC
Ah! I think I got it!

GDB is using boffset to check if there is virtual inheritance. However, this test case has an empty struct, so the offset for both members is the same! If any members are added to struct S, GDB correctly identifies the ambiguity.
Comment 17 Pedro Alves 2021-11-04 18:45:56 UTC
> I don't think it's the depth that confuses GDB, it seems to be the 
> no_unique_address.

Maybe unrelated, but FYI this last reproducer triggers this GCC bug:

 Bug 101378 - Negative DW_AT_data_member_location
 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101378

Still reproduces with today's gcc.  :-/
Comment 18 Guinevere Larsen 2021-11-04 19:40:58 UTC
> Maybe unrelated, but FYI this last reproducer triggers this GCC bug:

This is unrelated, the actual offset being correct can still point ambiguous things to the same spot

A quick patch to struct_field_searcher::update_value, to check if the values in the end of the inheritance path are the same results in the expected behavior. I'll work on the ambiguous test case and submit a new patch to the mailing list soon
Comment 19 Guinevere Larsen 2021-11-05 13:53:46 UTC
I've managed to spin up a patch to fix this, it's on the mailing list already: https://sourceware.org/pipermail/gdb-patches/2021-November/183145.html

I'd suggest that the title of the bug should be updated to something more representative of the actual bug, like "Incorrect ambiguity detection due to [[no_unique_address]]" or something like that
Comment 20 Sourceware Commits 2021-11-25 12:56:43 UTC
The master branch has been updated by Bruno Larsen <blarsen@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a41ad3474ceacba39e11c7478154c0e553784a01

commit a41ad3474ceacba39e11c7478154c0e553784a01
Author: Bruno Larsen <blarsen@redhat.com>
Date:   Fri Oct 29 17:56:28 2021 -0300

    PR gdb/28480: Improve ambiguous member detection
    
    Basic ambiguity detection assumes that when 2 fields with the same name
    have the same byte offset, it must be an unambiguous request. This is not
    always correct. Consider the following code:
    
    class empty { };
    
    class A {
    public:
      [[no_unique_address]] empty e;
    };
    
    class B {
    public:
      int e;
    };
    
    class C: public A, public B { };
    
    if we tried to use c.e in code, the compiler would warn of an ambiguity,
    however, since A::e does not demand an unique address, it gets the same
    address (and thus byte offset) of the members, making A::e and B::e have the
    same address. however, "print c.e" would fail to report the ambiguity,
    and would instead print it as an empty class (first path found).
    
    The new code solves this by checking for other found_fields that have
    different m_struct_path.back() (final class that the member was found
    in), despite having the same byte offset.
    
    The testcase gdb.cp/ambiguous.exp was also changed to test for this
    behavior.
Comment 21 Guinevere Larsen 2021-11-25 15:25:10 UTC
The commit mentioned fixes this bug
Comment 22 Sourceware Commits 2021-12-11 07:49:14 UTC
The gdb-11-branch branch has been updated by Joel Brobecker <brobecke@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b6fc91c70a43d96d02c065ec0a9be96f272dace6

commit b6fc91c70a43d96d02c065ec0a9be96f272dace6
Author: Bruno Larsen <blarsen@redhat.com>
Date:   Sat Dec 11 11:47:53 2021 +0400

    PR gdb/28480: Improve ambiguous member detection
    
    Basic ambiguity detection assumes that when 2 fields with the same name
    have the same byte offset, it must be an unambiguous request. This is not
    always correct. Consider the following code:
    
    class empty { };
    
    class A {
    public:
      [[no_unique_address]] empty e;
    };
    
    class B {
    public:
      int e;
    };
    
    class C: public A, public B { };
    
    if we tried to use c.e in code, the compiler would warn of an ambiguity,
    however, since A::e does not demand an unique address, it gets the same
    address (and thus byte offset) of the members, making A::e and B::e have the
    same address. however, "print c.e" would fail to report the ambiguity,
    and would instead print it as an empty class (first path found).
    
    The new code solves this by checking for other found_fields that have
    different m_struct_path.back() (final class that the member was found
    in), despite having the same byte offset.
    
    The testcase gdb.cp/ambiguous.exp was also changed to test for this
    behavior.
    
    gdb/ChangeLog:
    
            PR gdb/28480
            * valops.c (struct_field_searcher::update_result): Improve
            ambiguous member detection.
    
    gdb/testsuite/ChangeLog:
    
            PR gdb/28480
    
            Pushed by Joel Brobecker  <brobecker@adacore.com>
            * gdb.cp/ambiguous.cc: Add code to permit ambiguous member testing.
            * gdb.cp/ambiguous.exp: Add ambiguous member test.
    
    (cherry picked from commit a41ad3474ceacba39e11c7478154c0e553784a01)