[gnu.g++.bug] G++, anonymous unions and gdb

dld@JMW.LARCH.CS.CMU.EDU (David Detlefs) (08/15/89)

Michael et. al. --

Problem: G++ does not produce appropriate symbol table information for
anonymous unions.  Consider the following program:

--------------------------------------------------
struct foo {
  union {
    int i;
    char* j;
  };
};


void main() {
  foo f;
  f.i = 7;
  f.j = "foofoo";
}
--------------------------------------------------

G++ compiles this without complaint, and, as far as I can tell,
correctly.  However, if you compile it with -g, and run it under
gdb, you can get the following script:

b main
Breakpoint 1 at 0xb5: file bug2.c, line 10.
gdb% run
Starting program: /usr0/dld/testing/bug2 

Bpt 1, main (1, 2147481872, 2147481880) (bug2.c line 10)
10	  f.i = 7;
gdb% p f
$1 = {}
gdb% p f.i
there is no field named i
gdb%

The problem is that when G++ generates symbol-table information for
"struct foo" in dbxout.c, it decides that "foo" has no members,
because it has no named members at the top-level.

Below is a fix to dbxout.c that makes G++ produce symbol-table
information that makes gdb treat members of anonymous unions as if
they are top-level members of the classes in which they occur.  That
is, in the above case, you get

gdb% p f
$2 = {i = 0, j = 0x0}
gdb% p f.i
$3 = 0
gdb% 

Ideally, we would also like gdb to print out extra sets of curly
braces to indicate which members of f are parts of the same anonymous
union:

gdb% p f
$2 = {{i = 0, j = 0x0}}
gdb% p f.i
$3 = 0
gdb% 

...But I can't figure out a way to do this.  I suspect that it would
require gdb modifications, since gdb seems to expect each field of a
record to have a name.

In the fix below, note that is important that the solution be
recursive, so that cases such as

--------------------------------------------------
struct bar {
  union {
    int i;
    union {
      float f;
      char* s;
    };
  };
};
--------------------------------------------------

get treated correctly -- bar is considered to have three top-level
members i, f, and s that all start at the same offset.

Here is the fix to dbxout.c:

***After line 172, add:

static void dbxout_field ();

***Change the else clause that begins at line 439 to

	else
	  dbxout_field(tem, type);

***Add the following function:

/* Output a field of a record type to the asm file. */

static void dbxout_field (field, type)
     register tree field, type;
{
  if ( DECL_NAME (field) == 0 )
    if ( TREE_CODE( TREE_TYPE (field) ) == UNION_TYPE ) {
      /* An anonymous union.  Print out a field descriptor for each */
      /* member of the union. */
      tree tem2;
      for (tem2 = TYPE_FIELDS( TREE_TYPE (field) ); tem2;
	   tem2 = TREE_CHAIN (tem2)) {
	dbxout_field(tem2, type);
      }
      return;
    } else
      /* Is a null field, only there to take up space. */
      return;

  /* Otherwise, just output the description of the field. */
  if (use_gdb_dbx_extensions || TREE_CODE (field) != CONST_DECL) {
    /* Continue the line if necessary, but not before the first field.  */
    if (field != TYPE_FIELDS (type))
      CONTIN;
    fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (field)));
    CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (field)));

    if (use_gdb_dbx_extensions)
      {
	putc ('/', asmfile);
#ifdef TREE_PRIVATE
	putc ((TREE_PRIVATE (field) ? '0' :
	       TREE_PROTECTED (field) ? '1' : '2'),
	      asmfile);
#endif
	CHARS (2);
      }

    dbxout_type (TREE_TYPE (field), 0);
    if (TREE_CODE (field) == VAR_DECL)
      {
	if (use_gdb_dbx_extensions)
	  {
	    char *name = XSTR (XEXP (DECL_RTL (field), 0), 0);
	    if (name[0] == '*')
	      name += 1;
	    /* Adding 1 here only works on systems
	       which flush an initial underscore.  */
	    fprintf (asmfile, ":%s;", name+1);
	    CHARS (strlen (name));
	  }
	else
	  {
	    fprintf (asmfile, ",0,0;");
	  }
      }
    else if (TREE_CODE (field) == CONST_DECL)
      {
	/* Only get here if using GDB's extensions to DBX format.  */
	fprintf (asmfile, "~i%d;", TREE_INT_CST_LOW (DECL_INITIAL (field)));
      }
    else
      {
	fprintf (asmfile, ",%d,%d;", DECL_OFFSET (field),
		 TREE_INT_CST_LOW (DECL_SIZE (field)) *
		 DECL_SIZE_UNIT (field));
      }
    CHARS (23);
  }
}


Happier hacking.

Dave