Skip to content

Commit debf6f0

Browse files
dkorpelclaude
andcommitted
Fix #20004 - generate DW_TAG_enumerator for enum members in DWARF debug info
For D enums, the backend's SEenumlist was never populated and SENforward was never cleared, causing all enum types to appear as forward-referenced declarations with no children in DWARF output. Changes: - In visitEnum, use type_enum for all integral base types (not just int), and populate SEenumlist with member names and values when debug info is enabled. This allows GDB to show symbolic enum names. - In type_size, make TYenum delegate to its Tnext (base type) for the size. Previously TYenum was hardcoded as LONGSIZE (4 bytes), which caused incorrect parameter passing for e.g. ulong or ubyte enums. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cb0d0b1 commit debf6f0

File tree

6 files changed

+141
-52
lines changed

6 files changed

+141
-52
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
DWARF debug info now includes enum members
2+
3+
The DMD compiler now generates `DW_TAG_enumerator` entries for D enum members in DWARF debug info.
4+
Previously, enum types were always emitted as forward references with no member information.
5+
6+
Debuggers can now inspect enum member names and values for `int`-based enums.

compiler/src/dmd/backend/dwarfdbginf.d

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,64 @@ static if (1)
22142214

22152215
/* ======================= Type Index ============================== */
22162216

2217+
/* Emit DW_TAG_enumeration_type with DW_TAG_enumerator children for the given enum symbol.
2218+
* Params:
2219+
* s = enum Classsym with SEenumlist populated
2220+
* base_type = the integral base type determining size and signedness
2221+
* Returns: DWARF type index of the emitted DW_TAG_enumeration_type
2222+
*/
2223+
uint dwarf_enum_typidx(Symbol* s, type* base_type)
2224+
{
2225+
if (s.Stypidx)
2226+
return s.Stypidx;
2227+
2228+
uint sz = cast(uint)type_size(base_type);
2229+
2230+
uint code = DWARFAbbrev.write!([
2231+
DW_TAG_enumeration_type, DW_CHILDREN_yes,
2232+
DW_AT_name, DW_FORM_string,
2233+
DW_AT_byte_size, DW_FORM_data1,
2234+
]);
2235+
2236+
OutBuffer abuf;
2237+
abuf.writeByte(DW_TAG_enumerator);
2238+
abuf.writeByte(DW_CHILDREN_no);
2239+
abuf.writeByte(DW_AT_name);
2240+
abuf.writeByte(DW_FORM_string);
2241+
abuf.writeByte(DW_AT_const_value);
2242+
if (tyuns(base_type.Tty))
2243+
abuf.writeByte(DW_FORM_udata);
2244+
else
2245+
abuf.writeByte(DW_FORM_sdata);
2246+
abuf.writeByte(0);
2247+
abuf.writeByte(0);
2248+
uint membercode = dwarf_abbrev_code(abuf.buf, abuf.length());
2249+
2250+
uint idx = cast(uint)debug_info.buf.length();
2251+
debug_info.buf.writeuLEB128(code);
2252+
debug_info.buf.writeStringz(getSymName(s)); // DW_AT_name
2253+
debug_info.buf.writeByte(cast(ubyte)sz); // DW_AT_byte_size
2254+
2255+
foreach (sl2; ListRange(s.Senum.SEenumlist))
2256+
{
2257+
Symbol* sf = cast(Symbol*)list_ptr(sl2);
2258+
const value = cast(uint)el_tolong(sf.Svalue);
2259+
2260+
debug_info.buf.writeuLEB128(membercode);
2261+
debug_info.buf.writeStringz(getSymName(sf)); // DW_AT_name
2262+
if (tyuns(base_type.Tty))
2263+
debug_info.buf.writeuLEB128(value);
2264+
else
2265+
debug_info.buf.writesLEB128(value);
2266+
}
2267+
2268+
debug_info.buf.writeByte(0); // no more children
2269+
2270+
s.Stypidx = idx;
2271+
resetSyms.push(s);
2272+
return idx;
2273+
}
2274+
22172275
uint dwarf_typidx(type* t, Symbol* sym = null)
22182276
{
22192277
uint idx = 0;
@@ -2340,6 +2398,10 @@ static if (1)
23402398
}
23412399
}
23422400

2401+
// D enum: stored as base type for ABI, but has enum members attached for debug info
2402+
if (t.Tflags & TF.denum)
2403+
return dwarf_enum_typidx(t.Ttag, t);
2404+
23432405
immutable tym_t ty = tybasic(t.Tty);
23442406
// use cached basic type if it's not TYdarray or TYdelegate
23452407
if (!(t.Tnext && (ty == TYdarray || ty == TYdelegate)))
@@ -2988,8 +3050,6 @@ static if (1)
29883050
Symbol* s = t.Ttag;
29893051
enum_t* se = s.Senum;
29903052
type* tbase2 = s.Stype.Tnext;
2991-
uint sz = cast(uint)type_size(tbase2);
2992-
symlist_t sl;
29933053

29943054
if (s.Stypidx)
29953055
return s.Stypidx;
@@ -3008,50 +3068,7 @@ static if (1)
30083068
break; // don't set Stypidx
30093069
}
30103070

3011-
code = DWARFAbbrev.write!([
3012-
DW_TAG_enumeration_type, DW_CHILDREN_yes, // child (the subrange type)
3013-
DW_AT_name, DW_FORM_string,
3014-
DW_AT_byte_size, DW_FORM_data1,
3015-
]);
3016-
3017-
uint membercode;
3018-
OutBuffer abuf;
3019-
abuf.writeByte(DW_TAG_enumerator);
3020-
abuf.writeByte(DW_CHILDREN_no);
3021-
abuf.writeByte(DW_AT_name);
3022-
abuf.writeByte(DW_FORM_string);
3023-
abuf.writeByte(DW_AT_const_value);
3024-
if (tyuns(tbase2.Tty))
3025-
abuf.writeByte(DW_FORM_udata);
3026-
else
3027-
abuf.writeByte(DW_FORM_sdata);
3028-
abuf.writeByte(0);
3029-
abuf.writeByte(0);
3030-
membercode = dwarf_abbrev_code(abuf.buf, abuf.length());
3031-
3032-
idx = cast(uint)debug_info.buf.length();
3033-
debug_info.buf.writeuLEB128(code);
3034-
debug_info.buf.writeStringz(getSymName(s)); // DW_AT_name
3035-
debug_info.buf.writeByte(cast(ubyte)sz); // DW_AT_byte_size
3036-
3037-
foreach (sl2; ListRange(s.Senum.SEenumlist))
3038-
{
3039-
Symbol* sf = cast(Symbol*)list_ptr(sl2);
3040-
const value = cast(uint)el_tolong(sf.Svalue);
3041-
3042-
debug_info.buf.writeuLEB128(membercode);
3043-
debug_info.buf.writeStringz(getSymName(sf)); // DW_AT_name
3044-
if (tyuns(tbase2.Tty))
3045-
debug_info.buf.writeuLEB128(value);
3046-
else
3047-
debug_info.buf.writesLEB128(value);
3048-
}
3049-
3050-
debug_info.buf.writeByte(0); // no more children
3051-
3052-
s.Stypidx = idx;
3053-
resetSyms.push(s);
3054-
return idx; // no need to cache it
3071+
return dwarf_enum_typidx(s, tbase2);
30553072
}
30563073

30573074
default:

compiler/src/dmd/backend/type.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ enum TF : ushort
5555
static_ = 0x40, // TYarray: static dimension
5656
vla = 0x80, // TYarray: variable length array
5757
emptyexc = 0x100, // tyfunc(): empty exception specification
58+
denum = 0x200, // D enum: base type for ABI but Ttag points to enum Classsym for debug info
5859
}
5960

6061
public import dmd.backend.symbol : symbol_struct_addField, symbol_struct_addBitField, symbol_struct_hasBitFields, symbol_struct_addBaseClass;

compiler/src/dmd/glue/toctype.d

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ module dmd.glue.toctype;
1414
import core.stdc.stdio;
1515
import core.stdc.stdlib;
1616

17-
import dmd.backend.cc : Classsym, Symbol;
17+
import dmd.backend.cc : Classsym, enum_t, Symbol;
18+
import dmd.backend.cdef : SC;
19+
import dmd.backend.dlist : list_append;
20+
import dmd.backend.el : el_long;
21+
import dmd.backend.mem : mem_calloc;
22+
import dmd.backend.symbol : symbol_calloc, symbol_name;
1823
import dmd.backend.ty;
1924
import dmd.backend.type;
2025

@@ -253,9 +258,39 @@ type* Type_toCtype(Type t)
253258
(cast(type*)t.ctype).Tcount++;
254259
return cast(type*)t.ctype;
255260
}
256-
else if (symMemtype.toBasetype().ty == Tint32)
261+
else if (symMemtype.isIntegral())
257262
{
258-
t.ctype = type_enum(sym.toPrettyChars(true), Type_toCtype(symMemtype));
263+
type* basectype = Type_toCtype(symMemtype);
264+
if (driverParams.symdebug && sym.members)
265+
{
266+
// Use the base type for ABI (avoids TYenum hardcoded-to-4-bytes issues),
267+
// but attach a D enum Classsym via TF.denum so the DWARF backend can
268+
// emit DW_TAG_enumeration_type with member names and values.
269+
type* enumctype = type_alloc(tybasic(basectype.Tty));
270+
enumctype.Tcount++;
271+
enumctype.Tflags = cast(TF)(basectype.Tflags | TF.denum);
272+
import core.stdc.string : strlen;
273+
const name = sym.toPrettyChars(true);
274+
Symbol* s = symbol_calloc(name[0 .. strlen(name)]);
275+
s.Senum = cast(enum_t*)mem_calloc(enum_t.sizeof);
276+
s.Sclass = SC.enum_;
277+
s.Stype = enumctype;
278+
enumctype.Ttag = s;
279+
foreach (m; *sym.members)
280+
{
281+
EnumMember em = m.isEnumMember();
282+
if (!em)
283+
continue;
284+
Symbol* sf = symbol_name(em.ident.toString(), SC.enum_, basectype);
285+
sf.Svalue = el_long(totym(symMemtype.toBasetype()), em.value().toInteger());
286+
list_append(&s.Senum.SEenumlist, sf);
287+
}
288+
t.ctype = enumctype;
289+
}
290+
else
291+
{
292+
t.ctype = basectype;
293+
}
259294
}
260295
else
261296
{
@@ -270,12 +305,13 @@ type* Type_toCtype(Type t)
270305

271306
// Copy mutable version of backend type and add modifiers
272307
type* mctype = Type_toCtype(t.castMod(0));
273-
if (tybasic(mctype.Tty) == TYenum)
308+
if (mctype.Tflags & TF.denum)
274309
{
275310
Classsym* s = mctype.Ttag;
276311
assert(s);
277-
type* tr = type_allocn(TYenum, mctype.Tnext);
278-
tr.Ttag = s; // enum tag name
312+
type* tr = type_alloc(tybasic(mctype.Tty));
313+
tr.Tflags = mctype.Tflags;
314+
tr.Ttag = s;
279315
tr.Tcount++;
280316
tr.Tty |= modToTym(t.mod);
281317
return tr;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
DW_TAG_enumeration_type
2+
DW_AT_name : issue20004.Color
3+
DW_TAG_enumerator
4+
DW_AT_name : red
5+
DW_AT_const_value : 0
6+
DW_AT_name : green
7+
DW_AT_const_value : 1
8+
DW_AT_name : blue
9+
DW_AT_const_value : 2
10+
DW_AT_name : issue20004.Small
11+
DW_AT_byte_size : 1
12+
DW_AT_name : a
13+
DW_AT_const_value : 10
14+
DW_AT_name : b
15+
DW_AT_const_value : 20
16+
DW_AT_name : issue20004.Big
17+
DW_AT_byte_size : 8
18+
DW_AT_name : x
19+
DW_AT_const_value : 1
20+
DW_AT_name : y
21+
DW_AT_const_value : 1000000
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
enum Color { red, green, blue }
2+
enum Small : ubyte { a = 10, b = 20 }
3+
enum Big : ulong { x = 1, y = 1000000 }
4+
void main() {
5+
Color c = Color.red;
6+
Small s = Small.a;
7+
Big b = Big.y;
8+
}

0 commit comments

Comments
 (0)