[v2,PR,gdb/19893] Fix handling of synthetic C++ references

Message ID 1464019228-11131-1-git-send-email-martin.galvan@tallertechnologies.com
State New, archived
Headers

Commit Message

Martin Galvan May 23, 2016, 4 p.m. UTC
  https://sourceware.org/bugzilla/show_bug.cgi?id=19893

I've traced the main source of the problem to pieced_value_funcs.coerce_ref not being
implemented. Since gdb always assumes references are implemented as pointers, this
causes it to think that it's dealing with a NULL pointer, thus breaking any operations
involving synthetic references.

What I did here was implementing pieced_value_funcs.coerce_ref using some of the synthetic
pointer handling code from indirect_pieced_value, as Pedro suggested. I also made a few
adjustments to the reference printing code so that it correctly shows the "<synthetic pointer>"
string.

I also wrote some unit tests based on Dwarf::assemble; these took a while to make
because in most cases I needed a synthetic reference to a physical variable. Additionally,
I started working on a unit test for classes that have a vtable, but ran into a few issues
so that'll probably go in a future patch. One thing that should definitely be fixed is that
proc function_range (called for MACRO_AT_func) will always try to compile/link using gcc
with the default options instead of g++, thus breaking C++ compilations that require e.g. libstdc++.

I've fixed a few things Pedro told me. Notice the printing of '@address' with 'set print object on'
is not necessarily a bug (the address that'll be shown is the referenced variable's, not something
like 0x0). If you ask me, I think we can just leave it as a (documented) corner case.

Ok to commit?

gdb/ChangeLog:
2016-05-23  Martin Galvan  <martin.galvan@tallertechnologies.com>

	* dwarf2loc.c (coerce_pieced_ref, indirect_synthetic_pointer,
	fetch_const_value_from_synthetic_pointer): New functions.
	(indirect_pieced_value): Move lower half to indirect_synthetic_pointer.
	(pieced_value_funcs): Implement coerce_ref.
	* valops.c (value_addr): Call coerce_ref for synthetic references.
	* valprint.c (valprint_check_validity): Return true for synthetic
	references.
	(generic_val_print_ref): Don't print '@address' for synthetic
	references.

gdb/testsuite/ChangeLog:
2016-05-23  Martin Galvan  <martin.galvan@tallertechnologies.com>

	* gdb.dwarf2/implref.exp: Rename to...
	* gdb.dwarf2/implref-const.exp: ...this.  Also add more test statements.
	* gdb.dwarf2/implref-array.c: New file.
	* gdb.dwarf2/implref-array.exp: Likewise.
	* gdb.dwarf2/implref-global.c: Likewise.
	* gdb.dwarf2/implref-global.exp: Likewise.
	* gdb.dwarf2/implref-struct.c: Likewise.
	* gdb.dwarf2/implref-struct.exp: Likewise.
---
 gdb/dwarf2loc.c                                    | 128 ++++++++++-----
 gdb/testsuite/gdb.dwarf2/implref-array.c           |  27 ++++
 gdb/testsuite/gdb.dwarf2/implref-array.exp         | 168 ++++++++++++++++++++
 .../gdb.dwarf2/{implref.exp => implref-const.exp}  |  54 +++++--
 gdb/testsuite/gdb.dwarf2/implref-global.c          |  27 ++++
 gdb/testsuite/gdb.dwarf2/implref-global.exp        | 124 +++++++++++++++
 gdb/testsuite/gdb.dwarf2/implref-struct.c          |  43 +++++
 gdb/testsuite/gdb.dwarf2/implref-struct.exp        | 173 +++++++++++++++++++++
 gdb/valops.c                                       |  36 +++--
 gdb/valprint.c                                     |  32 +++-
 10 files changed, 739 insertions(+), 73 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-array.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-array.exp
 rename gdb/testsuite/gdb.dwarf2/{implref.exp => implref-const.exp} (57%)
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-global.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-global.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-struct.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-struct.exp
  

Comments

Pedro Alves May 24, 2016, 10:46 a.m. UTC | #1
Hi Martin,

This looks mostly good to me.  However ...

On 05/23/2016 05:00 PM, Martin Galvan wrote:

> I've fixed a few things Pedro told me. Notice the printing of '@address' with 'set print object on'
> is not necessarily a bug (the address that'll be shown is the referenced variable's, not something
> like 0x0). If you ask me, I think we can just leave it as a (documented) corner case.

... I still don't know what to think of this -- I simply don't understand it whether
you're doing this because it makes sense, or because doing otherwise would be hard
to do?

- Can you show an example output?  (set print object on/off, etc. whatever might be
  handy to clearly explain that that is about). 
  Pictures are really worth a thousand words.  :-)

- Is this covered by any testcase?  I looked for "object" in the whole patch and
  didn't seem to find it.

Thanks,
Pedro Alves
  
Martin Galvan May 24, 2016, 2:07 p.m. UTC | #2
Thanks for the answer!

On Tue, May 24, 2016 at 7:46 AM, Pedro Alves <palves@redhat.com> wrote:
> ... I still don't know what to think of this -- I simply don't understand it whether
> you're doing this because it makes sense, or because doing otherwise would be hard
> to do?

From a consistency point of view, it's probably not the right thing.
All of the synthetic pointer cases I've tested always show "<synthetic
pointer>" instead of "@address".

As for how to fix it, yeah, it would be hard. Or at least I don't know
how to do it off the top of my head. I'd have to make value_addr not
return a not_lval when passing it a synthetic ref, which I'm not sure
it's right either.

I *could*, however, manually call
value->location.computed.funcs->check_synthetic_pointer in
generic_val_print_ref instead of using value_bits_synthetic_pointer,
thus avoiding the check for lval_computed. But that's a bit ugly IMHO.

> - Can you show an example output?  (set print object on/off, etc. whatever might be
>   handy to clearly explain that that is about).
>   Pictures are really worth a thousand words.  :-)

Sure:

(gdb) set print object off
(gdb) print ref
$3 = (S &) <synthetic pointer>: {
  a = 0,
  b = 1,
  c = 2
}
(gdb) set print object on
(gdb) print ref
$4 = (S &) @0x601038: {
  a = 0,
  b = 1,
  c = 2
}

Here, 0x601038 is the address of the structure 'ref' is referencing.
This is consistent with the output for non-synthetic references, where
the referenced value's address is shown.

> - Is this covered by any testcase?  I looked for "object" in the whole patch and
>   didn't seem to find it.

Not that I know of. Should I add a test for this to implref-struct?
  
Pedro Alves May 24, 2016, 2:51 p.m. UTC | #3
On 05/24/2016 03:07 PM, Martin Galvan wrote:
> Thanks for the answer!
> 
> On Tue, May 24, 2016 at 7:46 AM, Pedro Alves <palves@redhat.com> wrote:
>> ... I still don't know what to think of this -- I simply don't understand it whether
>> you're doing this because it makes sense, or because doing otherwise would be hard
>> to do?
> 
> From a consistency point of view, it's probably not the right thing.
> All of the synthetic pointer cases I've tested always show "<synthetic
> pointer>" instead of "@address".

But normal pointers don't print @address either, only references do.

Not printing "@address" with "set print object off" seems like
hiding information from the user, information that we could show.
We always print it for non-synthetic references, AFAICS.

> Or at least I don't know
> how to do it off the top of my head. I'd have to make value_addr not
> return a not_lval when passing it a synthetic ref, which I'm not sure
> it's right either.

Your comment in the patch, in generic_val_print_ref, reads:

+	 if options->objectprint is true, c_value_print will call value_addr
+	 on the reference, which coerces synthetic references and returns a
+	 'not_lval'.  */

So if that works, I don't understand -- wouldn't calling value_addr
or coerce_ref in generic_val_print_ref if you have a synthetic
reference, or any reference even, be what you'd want?

> 
> I *could*, however, manually call
> value->location.computed.funcs->check_synthetic_pointer in
> generic_val_print_ref instead of using value_bits_synthetic_pointer,
> thus avoiding the check for lval_computed. But that's a bit ugly IMHO.

I don't understand this one.  Only lval_computed values have a 
"location.computed.funcs" to call.

> 
>> - Can you show an example output?  (set print object on/off, etc. whatever might be
>>   handy to clearly explain that that is about).
>>   Pictures are really worth a thousand words.  :-)

So is the problem that this bit:

   if (options->addressprint)
     {
      CORE_ADDR addr
	= extract_typed_address (valaddr + embedded_offset, type);

doesn't work / doesn't make sense with synthetic pointers?

Should we be calling value_addr instead?

Or are we perhaps missing a lval_funcs method?  (Ideally, all
value properties/methods would go through a vtable like
lval_funcs; think "making struct value a proper C++ class" going
forward.)

> Here, 0x601038 is the address of the structure 'ref' is referencing.
> This is consistent with the output for non-synthetic references, where
> the referenced value's address is shown.
> 
>> - Is this covered by any testcase?  I looked for "object" in the whole patch and
>>   didn't seem to find it.
> 
> Not that I know of. Should I add a test for this to implref-struct?

I don't know where, but I think this should indeed be covered by
tests somewhere.

Thanks,
Pedro Alves
  
Martin Galvan May 24, 2016, 8:35 p.m. UTC | #4
I've just realized that my comment isn't entirely clear. The
"@address" string is shown only for structure types, not for regular
variables, arrays or such. It makes sense because only those are
affected by set print object, but the comment doesn't explicitly say
so.

I don't know whether it'd be preferable to have <synthetic pointer>
everywhere as opposed to @address. I guess @address is more consistent
with non-synthetic references, and it also hides the fact they're
synthetic from the user, but there are cases (such as
DW_AT_const_value) where @address wouldn't work/make sense.

So I think before proceeding we should decide which output is better.
Perhaps we could show @address whenever possible, and <synthetic
pointer> for the corner cases?

On Tue, May 24, 2016 at 11:51 AM, Pedro Alves <palves@redhat.com> wrote:
> But normal pointers don't print @address either, only references do.

Yeah, I was referring to references :)

> Not printing "@address" with "set print object off" seems like
> hiding information from the user, information that we could show.
> We always print it for non-synthetic references, AFAICS.

Yes, we do.

> Your comment in the patch, in generic_val_print_ref, reads:
>
> +        if options->objectprint is true, c_value_print will call value_addr
> +        on the reference, which coerces synthetic references and returns a
> +        'not_lval'.  */
>
> So if that works, I don't understand -- wouldn't calling value_addr
> or coerce_ref in generic_val_print_ref if you have a synthetic
> reference, or any reference even, be what you'd want?

If I'm not mistaken, doing that would cause us to always print
"@address". Which again, may be in fact the right thing to do.

>> I *could*, however, manually call
>> value->location.computed.funcs->check_synthetic_pointer in
>> generic_val_print_ref instead of using value_bits_synthetic_pointer,
>> thus avoiding the check for lval_computed. But that's a bit ugly IMHO.
>
> I don't understand this one.  Only lval_computed values have a
> "location.computed.funcs" to call.

Yeah, you're right. For a moment I thought all values had an
lval_funcs member, but only computed ones do.

> So is the problem that this bit:
>
>    if (options->addressprint)
>      {
>       CORE_ADDR addr
>         = extract_typed_address (valaddr + embedded_offset, type);
>
> doesn't work / doesn't make sense with synthetic pointers?

Exactly. IIRC extract_typed_address would return zero (or maybe just
garbage?), because valaddr would actually be a pointer to the
synthetic pointer's lval_funcs instead of a target address.

On a related note, it'd be great (for debugging at least!) if unions
such as this had at least a discriminant of sorts.

> Should we be calling value_addr instead?

Perhaps. I stuck to calling extract_typed_address because I tried to
change as little code as possible. I'm seeing that
generic_val_print_ref has a path which coerces the reference anyway,
so we could always coerce it, get its address, and only print the
referenced value when required. I'd have to test it thoroughly to be
sure, though.

> Or are we perhaps missing a lval_funcs method?  (Ideally, all
> value properties/methods would go through a vtable like
> lval_funcs; think "making struct value a proper C++ class" going
> forward.)

Right now I don't think so. The existing methods should be enough to
handle these cases. Speaking of which, are there any plans to rewrite
this sort of object-oriented code in C++? I'd love to take a shot at
this in the future.

> I don't know where, but I think this should indeed be covered by
> tests somewhere.

Ok.
  
Pedro Alves May 25, 2016, 6:23 p.m. UTC | #5
On 05/24/2016 09:35 PM, Martin Galvan wrote:

> So I think before proceeding we should decide which output is better.
> Perhaps we could show @address whenever possible, and <synthetic
> pointer> for the corner cases?

Yes, I think so.  Synthetic pointers are really
an implementation detail, not really something users care about.

> On a related note, it'd be great (for debugging at least!) if unions
> such as this had at least a discriminant of sorts.

It's the value's lval type.  :-)

> Right now I don't think so. The existing methods should be enough to
> handle these cases. Speaking of which, are there any plans to rewrite
> this sort of object-oriented code in C++? I'd love to take a shot at
> this in the future.

Certainly, that'd be welcome.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
index bfe1173..4776e52 100644
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -2061,6 +2061,66 @@  get_frame_address_in_block_wrapper (void *baton)
   return get_frame_address_in_block ((struct frame_info *) baton);
 }
 
+/* Fetch a DW_AT_const_value through a synthetic pointer.  */
+
+static struct value *
+fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
+					  struct dwarf2_per_cu_data *per_cu,
+					  struct type *type)
+{
+  struct value *result = NULL;
+  struct obstack temp_obstack;
+  struct cleanup *cleanup;
+  const gdb_byte *bytes;
+  LONGEST len;
+
+  obstack_init (&temp_obstack);
+  cleanup = make_cleanup_obstack_free (&temp_obstack);
+  bytes = dwarf2_fetch_constant_bytes (die, per_cu, &temp_obstack, &len);
+
+  if (bytes != NULL)
+    {
+      if (byte_offset >= 0
+	  && byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) <= len)
+	{
+	  bytes += byte_offset;
+	  result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
+	}
+      else
+	invalid_synthetic_pointer ();
+    }
+  else
+    result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
+
+  do_cleanups (cleanup);
+
+  return result;
+}
+
+/* Fetch the value pointed to by a synthetic pointer.  */
+
+static struct value *
+indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
+			    struct dwarf2_per_cu_data *per_cu,
+			    struct frame_info *frame, struct type *type)
+{
+  /* Fetch the location expression of the DIE we're pointing to.  */
+  struct dwarf2_locexpr_baton baton
+    = dwarf2_fetch_die_loc_sect_off (die, per_cu,
+				     get_frame_address_in_block_wrapper, frame);
+
+  /* If pointed-to DIE has a DW_AT_location, evaluate it and return the
+     resulting value.  Otherwise, it may have a DW_AT_const_value instead,
+     or it may've been optimized out.  */
+  if (baton.data != NULL)
+    return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
+					  baton.data, baton.size, baton.per_cu,
+					  byte_offset);
+  else
+    return fetch_const_value_from_synthetic_pointer (die, byte_offset, per_cu,
+						     type);
+}
+
 /* An implementation of an lval_funcs method to indirect through a
    pointer.  This handles the synthetic pointer case when needed.  */
 
@@ -2115,6 +2175,7 @@  indirect_pieced_value (struct value *value)
       break;
     }
 
+  gdb_assert (piece != NULL);
   frame = get_selected_frame (_("No frame selected."));
 
   /* This is an offset requested by GDB, such as value subscripts.
@@ -2132,43 +2193,40 @@  indirect_pieced_value (struct value *value)
 					TYPE_LENGTH (type), byte_order);
   byte_offset += piece->v.ptr.offset;
 
-  gdb_assert (piece);
-  baton
-    = dwarf2_fetch_die_loc_sect_off (piece->v.ptr.die, c->per_cu,
-				     get_frame_address_in_block_wrapper,
-				     frame);
+  return indirect_synthetic_pointer (piece->v.ptr.die, byte_offset, c->per_cu,
+				     frame, type);
+}
 
-  if (baton.data != NULL)
-    return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
-					  baton.data, baton.size, baton.per_cu,
-					  byte_offset);
+/* Implementation of the coerce_ref method of lval_funcs for synthetic C++
+   references.  */
 
-  {
-    struct obstack temp_obstack;
-    struct cleanup *cleanup;
-    const gdb_byte *bytes;
-    LONGEST len;
-    struct value *result;
-
-    obstack_init (&temp_obstack);
-    cleanup = make_cleanup_obstack_free (&temp_obstack);
-
-    bytes = dwarf2_fetch_constant_bytes (piece->v.ptr.die, c->per_cu,
-					 &temp_obstack, &len);
-    if (bytes == NULL)
-      result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
-    else
-      {
-	if (byte_offset < 0
-	    || byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > len)
-	  invalid_synthetic_pointer ();
-	bytes += byte_offset;
-	result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
-      }
+static struct value *
+coerce_pieced_ref (const struct value *value)
+{
+  struct type *type = check_typedef (value_type (value));
 
-    do_cleanups (cleanup);
-    return result;
-  }
+  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
+				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+    {
+      const struct piece_closure *closure
+	= (struct piece_closure *) value_computed_closure (value);
+      struct frame_info *frame
+	= get_selected_frame (_("No frame selected."));
+
+      /* gdb represents synthetic pointers as pieced values with a single
+       piece.  */
+      gdb_assert (closure != NULL);
+      gdb_assert (closure->n_pieces == 1);
+
+      return indirect_synthetic_pointer (closure->pieces->v.ptr.die,
+					 closure->pieces->v.ptr.offset,
+					 closure->per_cu, frame, type);
+    }
+  else
+    {
+      /* Else: not a synthetic reference; do nothing.  */
+      return NULL;
+    }
 }
 
 static void *
@@ -2206,7 +2264,7 @@  static const struct lval_funcs pieced_value_funcs = {
   read_pieced_value,
   write_pieced_value,
   indirect_pieced_value,
-  NULL,	/* coerce_ref */
+  coerce_pieced_ref,
   check_pieced_synthetic_pointer,
   copy_pieced_value_closure,
   free_pieced_value_closure
diff --git a/gdb/testsuite/gdb.dwarf2/implref-array.c b/gdb/testsuite/gdb.dwarf2/implref-array.c
new file mode 100644
index 0000000..2470412
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-array.c
@@ -0,0 +1,27 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+/* Test program for synthetic C++ references to arrays.  */
+
+int array[5] = {0, 1, 2, 3, 4};
+
+int
+main (void)
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-array.exp b/gdb/testsuite/gdb.dwarf2/implref-array.exp
new file mode 100644
index 0000000..1646785
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-array.exp
@@ -0,0 +1,168 @@ 
+# Copyright 2016 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 <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global array whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-array.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-array".  srcfile2 is "implref-array.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-array program.
+# Any program would do, but since we already have implref-array
+# specifically for this testcase, might as well use that.
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    untested ${testfile}.exp
+    return -1
+}
+
+set array_length [get_valueof "/u" "sizeof(array) / sizeof(array\[0\])" -1]
+
+# Create the DWARF.  We need a regular variable which represents the array, and
+# a reference to it that'll be marked with DW_OP_GNU_implicit_pointer.
+# The variable must be global so that its name is an exported symbol that we
+# can reference from the DWARF using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile array_length
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label sizetype_label array_label variable_label ref_label
+	    set int_size [get_sizeof "int" -1]
+	    set upper_bound [expr ${array_length} - 1]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" -1]
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    sizetype_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_unsigned}
+		{DW_AT_name "sizetype"}
+	    }
+
+	    array_label: DW_TAG_array_type {
+		    {DW_AT_type :${int_label}}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :${sizetype_label}}
+		    {DW_AT_lower_bound 0 DW_FORM_udata}
+		    {DW_AT_upper_bound ${upper_bound} DW_FORM_udata}
+		}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${array_label}}
+	    }
+
+	    variable_label: DW_TAG_variable {
+		{DW_AT_name "array"}
+		{DW_AT_type :${array_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "array"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+		DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${ref_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# This matches e.g. '(int (&)[5])'
+set ref_type [format {\(int \(&\)\[%d\]\)} ${array_length}]
+
+# This matches e.g. '(int (*)[5])'
+set ptr_type [format {\(int \(\*\)\[%d\]\)} ${array_length}]
+
+# Contents of the array.  Trim leading/trailing whitespace, '{' and '}'
+# since they confuse TCL to no end.
+set contents [get_valueof "" "array" ""]
+set contents [string trim ${contents}]
+set contents [string trim ${contents} "{}"]
+
+# Doing 'print ref' should show us e.g. '(int (&)[5]) <synthetic pointer>: {0, 1, 2, 3, 4}'.
+gdb_test "print ref" " = ${ref_type} <synthetic pointer>: \\{${contents}\\}"
+
+# Doing 'print &ref' should show us e.g. '(int (*)[5]) 0xdeadbeef <array>'.
+gdb_test "print &ref" " = ${ptr_type} \[\[:xdigit:\]x\]+ <array>"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.  Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
+
+# Test assignment through the synthetic reference.
+set first_value 10
+gdb_test_no_output "set (ref\[0\] = ${first_value})"
+
+# This matches '{10, 1, 2, 3, 4}'.
+set new_contents [format {\{%d, 1, 2, 3, 4\}} ${first_value}]
+
+# Doing 'print ref' should now show us e.g.
+# '(int (&)[5]) <synthetic pointer>: {10, 1, 2, 3, 4}'.
+gdb_test "print ref" " = ${ref_type} <synthetic pointer>: ${new_contents}" "print ref after assignment"
+gdb_test "print array" " = ${new_contents}" "print array after assignment"
+
+# Test treating the array as a pointer.
+set second_value 20
+set new_contents [format {\{%d, %d, 3, 4\}} ${first_value} ${second_value}]
+
+gdb_test "print *ref" " = ${first_value}"
+gdb_test_no_output "set (*(ref + 1) = ${second_value})"
+gdb_test "print ref\[1\]" " = ${second_value}"
+gdb_test "print array" " = ${new_contents}" "print array after second assignment"
diff --git a/gdb/testsuite/gdb.dwarf2/implref.exp b/gdb/testsuite/gdb.dwarf2/implref-const.exp
similarity index 57%
rename from gdb/testsuite/gdb.dwarf2/implref.exp
rename to gdb/testsuite/gdb.dwarf2/implref-const.exp
index ca4766e..1a93179 100644
--- a/gdb/testsuite/gdb.dwarf2/implref.exp
+++ b/gdb/testsuite/gdb.dwarf2/implref-const.exp
@@ -13,45 +13,62 @@ 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# Test C++ references marked with DW_OP_GNU_implicit_pointer.
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a DW_AT_const_value.
 
-# TODO: Add more test statements after fixing bug #19893:
-# https://sourceware.org/bugzilla/show_bug.cgi?id=19893.
+if [skip_cplus_tests] {
+    continue
+}
 
 load_lib dwarf.exp
 
 # This test can only be run on targets which support DWARF-2 and use gas.
-if {![dwarf2_support]} {
+if ![dwarf2_support] {
     return 0
 }
 
-# We'll place the output of Dwarf::assemble in implref.S.
+# We'll place the output of Dwarf::assemble in implref-const.S.
 standard_testfile main.c .S
 
-# ${testfile} is now "implref".  srcfile2 is "implref.S".
+# ${testfile} is now "implref-const".  srcfile2 is "implref-const.S".
 set executable ${testfile}
 set asm_file [standard_output_file ${srcfile2}]
 
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-const program.
+# Any program would do, but since we already have implref-const
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] {
+    untested ${testfile}.exp
+    return -1
+}
+
 # Create the DWARF.  We need a regular variable and a reference to it that'll
 # be marked with DW_OP_GNU_implicit_pointer.
-Dwarf::assemble $asm_file {
+Dwarf::assemble ${asm_file} {
     global srcdir subdir srcfile
 
-    cu { version 3 addr_size 4 } {
+    cu {} {
 	DW_TAG_compile_unit {
-	    {DW_AT_producer "GNU C++ 4.8.4"}
 	    {DW_AT_language @DW_LANG_C_plus_plus}
 	} {
 	    declare_labels int_label const_label variable_label ref_label
+	    set int_size [get_sizeof "int" -1]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" -1]
+	    set var_value 42
 
 	    int_label: DW_TAG_base_type {
-		{DW_AT_byte_size 4 DW_FORM_udata}
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
 		{DW_AT_encoding @DW_ATE_signed}
 		{DW_AT_name "int"}
 	    }
 
 	    ref_label: DW_TAG_reference_type {
-		{DW_AT_byte_size 4 DW_FORM_udata}
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
 		{DW_AT_type :${int_label}}
 	    }
 
@@ -67,7 +84,7 @@  Dwarf::assemble $asm_file {
 		variable_label: DW_TAG_variable {
 		    {DW_AT_name "var"}
 		    {DW_AT_type :${int_label}}
-		    {DW_AT_const_value 42 DW_FORM_udata}
+		    {DW_AT_const_value ${var_value} DW_FORM_udata}
 		}
 
 		DW_TAG_variable {
@@ -80,7 +97,7 @@  Dwarf::assemble $asm_file {
     }
 }
 
-if [prepare_for_testing ${testfile}.exp ${executable} "${asm_file} ${srcfile}" {}] {
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
     return -1
 }
 
@@ -89,10 +106,15 @@  if ![runto_main] {
     return -1
 }
 
-gdb_test "print ref" " = \\(int &\\) <synthetic pointer>" "print ref"
-gdb_test "print &ref" " = \\(int \\*\\) <synthetic pointer>" "print &ref"
+# Doing 'print ref' should show us e.g. '(int &) <synthetic pointer>: 42'.
+set value_output {\(int &\) <synthetic pointer>: \d+}
+gdb_test "print ref" " = ${value_output}"
+
+# The variable isn't located in memory, thus we can't take its address.
+gdb_test "print &var" "Can't take address of \"var\" which isn't an lvalue."
+gdb_test "print &ref" "Attempt to take address of value not located in memory."
 
 # gdb assumes C++ references are implemented as pointers, and print &(&ref)
 # shows us the underlying pointer's address.
 # Since in this case there's no physical pointer, gdb should tell us so.
-gdb_test "print &\(&ref\)" "Attempt to take address of value not located in memory." "print &(&ref)"
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
diff --git a/gdb/testsuite/gdb.dwarf2/implref-global.c b/gdb/testsuite/gdb.dwarf2/implref-global.c
new file mode 100644
index 0000000..7814a30
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-global.c
@@ -0,0 +1,27 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+/* Test program for synthetic C++ references to global variables.  */
+
+int global_var = 42;
+
+int
+main (void)
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-global.exp b/gdb/testsuite/gdb.dwarf2/implref-global.exp
new file mode 100644
index 0000000..e9c6c48
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-global.exp
@@ -0,0 +1,124 @@ 
+# Copyright 2016 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 <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global variable whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-global.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-global".  srcfile2 is "implref-global.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-global program.
+# Any program would do, but since we already have implref-global
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] {
+    untested ${testfile}.exp
+    return -1
+}
+
+# Create the DWARF.  We need a regular variable and a reference to it that'll
+# be marked with DW_OP_GNU_implicit_pointer.  The variable must be global so
+# that its name is an exported symbol that we can reference from the DWARF
+# using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label variable_label ref_label
+	    set int_size [get_sizeof "int" -1]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" -1]
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${int_label}}
+	    }
+
+	    variable_label: DW_TAG_variable {
+		{DW_AT_name "global_var"}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "global_var"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+		DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${ref_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# Doing 'print ref' should show us e.g. '(int &) <synthetic pointer>: 42'.
+set value_output {\(int &\) <synthetic pointer>: \d+}
+gdb_test "print ref" " = ${value_output}"
+
+# Doing 'print &ref' should show us e.g. '(int *) 0xdeadbeef <global_var>'.
+set addr_output {\(int \*\) [[:xdigit:]x]+ <global_var>}
+gdb_test "print &ref" " = ${addr_output}"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.  Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
+
+# Test assignment through the synthetic reference.
+set new_value 10
+gdb_test_no_output "set (ref = ${new_value})"
+gdb_test "print ref" " = \\(int &\\) <synthetic pointer>: ${new_value}" "print ref after assignment"
+gdb_test "print global_var" " = ${new_value}" "print global_var after assignment"
diff --git a/gdb/testsuite/gdb.dwarf2/implref-struct.c b/gdb/testsuite/gdb.dwarf2/implref-struct.c
new file mode 100644
index 0000000..cc4f730
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-struct.c
@@ -0,0 +1,43 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+/* Test program for synthetic C++ references to structs.  */
+
+struct S {
+  int a;
+  int b;
+  int c;
+};
+
+struct S s1 = {
+  0,
+  1,
+  2
+};
+
+struct S s2 = {
+  10,
+  11,
+  12
+};
+
+int
+main (void)
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-struct.exp b/gdb/testsuite/gdb.dwarf2/implref-struct.exp
new file mode 100644
index 0000000..e21f625
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-struct.exp
@@ -0,0 +1,173 @@ 
+# Copyright 2016 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 <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global struct whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-struct.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-struct".  srcfile2 is "implref-struct.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-struct program.
+# Any program would do, but since we already have implref-struct
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug c++}] {
+    untested ${testfile}.exp
+    return -1
+}
+
+# Create the DWARF.  We need a regular variable for the struct and a reference
+# to it that'll be marked with DW_OP_GNU_implicit_pointer. The variable must be
+# global so that its name is an exported symbol that can we can reference from
+# the DWARF using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label struct_label variable_label ref_label
+	    set int_size [get_sizeof "int" -1]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" -1]
+	    set S_size [get_sizeof "S" -1]
+
+	    # The compiler shouldn't introduce structure padding here.
+	    set b_offset 4
+	    set c_offset 8
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    struct_label: DW_TAG_structure_type {
+		{DW_AT_name "S"}
+		{DW_AT_byte_size ${S_size} DW_FORM_udata}
+	    } {
+		DW_TAG_member {
+		    {DW_AT_name "a"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_data_member_location 0 DW_FORM_udata}
+		}
+
+		DW_TAG_member {
+		    {DW_AT_name "b"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_data_member_location ${b_offset} DW_FORM_udata}
+		}
+		
+		DW_TAG_member {
+		    {DW_AT_name "c"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_data_member_location ${c_offset} DW_FORM_udata}
+		}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${struct_label}}
+	    }
+
+	    variable_label: DW_TAG_variable {
+		{DW_AT_name "s1"}
+		{DW_AT_type :${struct_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "s1"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_variable {
+		{DW_AT_name "s2"}
+		{DW_AT_type :${struct_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "s2"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+	        DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${ref_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# Returns the struct members.
+proc get_members {var default} {
+	set members [get_valueof "" ${var} ${default}]
+	
+	# Trim leading/trailing whitespace, '{' and '}' since they confuse TCL
+	# to no end.
+	set members [string trim ${members}]
+	set members [string trim ${members} "{}"]
+
+	return ${members}
+}
+
+# Values of the struct members.
+set s1_members [get_members "s1" "\{a = 0, b = 1, c = 2\}"]
+set s2_members [get_members "s2" "\{a = 10, b = 11, c = 12\}"]
+
+# Doing 'print ref' should show us e.g. '(S &) <synthetic pointer>: {a = 0, b = 1, c = 2}'.
+gdb_test "print ref" " = \\(S &\\) <synthetic pointer>: \\{${s1_members}\\}"
+
+# Doing 'print &ref' should show us e.g. '(S *) 0xdeadbeef <s1>'.
+set addr_output {\(S \*\) [[:xdigit:]x]+ <s1>}
+gdb_test "print &ref" " = ${addr_output}"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.  Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
+
+# Test assignment through the synthetic reference.
+gdb_test_no_output "set (ref = s2)"
+gdb_test "print ref" " = \\(S &\\) <synthetic pointer>: \\{${s2_members}\\}" "print ref after assignment"
+gdb_test "print s1" " = \\{${s2_members}\\}" "print s1 after assignment"
diff --git a/gdb/valops.c b/gdb/valops.c
index 71fb1b3..7f9cb93 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1465,22 +1465,28 @@  value_addr (struct value *arg1)
 
   if (TYPE_CODE (type) == TYPE_CODE_REF)
     {
-      /* Copy the value, but change the type from (T&) to (T*).  We
-	 keep the same location information, which is efficient, and
-	 allows &(&X) to get the location containing the reference.
-	 Do the same to its enclosing type for consistency.  */
-      struct type *type_ptr
-        = lookup_pointer_type (TYPE_TARGET_TYPE (type));
-      struct type *enclosing_type
-        = check_typedef (value_enclosing_type (arg1));
-      struct type *enclosing_type_ptr
-        = lookup_pointer_type (TYPE_TARGET_TYPE (enclosing_type));
-
-      arg2 = value_copy (arg1);
-      deprecated_set_value_type (arg2, type_ptr);
-      set_value_enclosing_type (arg2, enclosing_type_ptr);
+      if (value_bits_synthetic_pointer (arg1, value_embedded_offset (arg1),
+	  TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+	arg1 = coerce_ref (arg1);
+      else
+	{
+	  /* Copy the value, but change the type from (T&) to (T*).  We
+	     keep the same location information, which is efficient, and
+	     allows &(&X) to get the location containing the reference.
+	     Do the same to its enclosing type for consistency.  */
+	  struct type *type_ptr
+	    = lookup_pointer_type (TYPE_TARGET_TYPE (type));
+	  struct type *enclosing_type
+	    = check_typedef (value_enclosing_type (arg1));
+	  struct type *enclosing_type_ptr
+	    = lookup_pointer_type (TYPE_TARGET_TYPE (enclosing_type));
+
+	  arg2 = value_copy (arg1);
+	  deprecated_set_value_type (arg2, type_ptr);
+	  set_value_enclosing_type (arg2, enclosing_type_ptr);
 
-      return arg2;
+	  return arg2;
+	}
     }
   if (TYPE_CODE (type) == TYPE_CODE_FUNC)
     return value_coerce_function (arg1);
diff --git a/gdb/valprint.c b/gdb/valprint.c
index cea69f3..eb57a53 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -336,7 +336,9 @@  valprint_check_validity (struct ui_file *stream,
 					TARGET_CHAR_BIT * TYPE_LENGTH (type)))
 	{
 	  fputs_filtered (_("<synthetic pointer>"), stream);
-	  return 0;
+
+	  /* C++ references should be valid even if they're synthetic.  */
+	  return TYPE_CODE (type) == TYPE_CODE_REF;
 	}
 
       if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type)))
@@ -497,13 +499,29 @@  generic_val_print_ref (struct type *type, const gdb_byte *valaddr,
 
   if (options->addressprint)
     {
-      CORE_ADDR addr
-	= extract_typed_address (valaddr + embedded_offset, type);
+      const int is_synthetic
+	= value_bits_synthetic_pointer (original_value,
+					TARGET_CHAR_BIT * embedded_offset,
+					TARGET_CHAR_BIT * TYPE_LENGTH (type));
+      if (!is_synthetic)
+	{
+	  CORE_ADDR addr
+	    = extract_typed_address (valaddr + embedded_offset, type);
 
-      fprintf_filtered (stream, "@");
-      fputs_filtered (paddress (gdbarch, addr), stream);
-      if (options->deref_ref)
-	fputs_filtered (": ", stream);
+	  fprintf_filtered (stream, "@");
+	  fputs_filtered (paddress (gdbarch, addr), stream);
+	}
+      /* Else, we have a synthetic reference.  Don't print '@address'; we'll
+	 show '<synthetic pointer>' instead through valprint_check_validity.
+	 Notice however that 'set print object on' will still show '@address'.
+	 This happens because value_bits_synthetic_pointer returns false if
+	 original value is not 'lval_computed'.  While that's usually the case,
+	 if options->objectprint is true, c_value_print will call value_addr
+	 on the reference, which coerces synthetic references and returns a
+	 'not_lval'.  */
+
+	if (options->deref_ref)
+	  fputs_filtered (": ", stream);
     }
   /* De-reference the reference.  */
   if (options->deref_ref)