diff --git a/changelog/dmd.dwarf-enum-members.dd b/changelog/dmd.dwarf-enum-members.dd new file mode 100644 index 000000000000..a0e2c2ec5fc2 --- /dev/null +++ b/changelog/dmd.dwarf-enum-members.dd @@ -0,0 +1,6 @@ +DWARF debug info now includes enum members + +The DMD compiler now generates `DW_TAG_enumerator` entries for D enum members in DWARF debug info. +Previously, enum types were always emitted as forward references with no member information. + +Debuggers can now inspect enum member names and values for `int`-based enums. diff --git a/compiler/src/dmd/backend/dcgcv.d b/compiler/src/dmd/backend/dcgcv.d index 56c20f300a3c..108f0fb17eb0 100644 --- a/compiler/src/dmd/backend/dcgcv.d +++ b/compiler/src/dmd/backend/dcgcv.d @@ -959,14 +959,17 @@ idx_t cv4_struct(Classsym* s,int flags) return s.Stypidx; } +/* Write a forward-reference enum record for the linker to fold with + * the full definition emitted by cv4_Denum (via toDebug). + * Params: + * s = enum tag symbol + * bty = CV base type code for the enum's integer base type + */ @trusted -private uint cv4_fwdenum(type* t) +private uint cv4_fwdenum(Symbol* s, uint bty) { - Symbol* s = t.Ttag; - - // write a forward reference enum record that is enough for the linker to - // fold with original definition from EnumDeclaration - uint bty = dttab4[tybasic(t.Tnext.Tty)]; + if (s.Stypidx) + return s.Stypidx; const id = prettyident(s); uint len = config.fulltypes == CV8 ? 14 : 10; debtyp_t* d = debtyp_alloc(len + cv_stringbytes(id)); @@ -981,7 +984,7 @@ private uint cv4_fwdenum(type* t) break; case CV4: - TOWORD(d.data.ptr,LF_ENUM); + TOWORD(d.data.ptr, LF_ENUM); TOWORD(d.data.ptr + 2, 0); // count TOWORD(d.data.ptr + 4, bty); // memtype TOLONG(d.data.ptr + 6, 0); // fieldlist @@ -1055,6 +1058,8 @@ uint cv4_typidx(type* t) if (!t) return dttab4[TYint]; // assume int type_debug(t); + if (t.Tflags & TF.denum) + return cv4_fwdenum(t.Ttag, dttab4[tybasic(t.Tty)]); next = cv4_typidx(t.Tnext); tycv = t.Tty; tym = tybasic(tycv); @@ -1425,7 +1430,7 @@ else { } else - typidx = cv4_fwdenum(t); + typidx = cv4_fwdenum(t.Ttag, dttab4[tybasic(t.Tnext.Tty)]); break; case TYref: diff --git a/compiler/src/dmd/backend/dwarfdbginf.d b/compiler/src/dmd/backend/dwarfdbginf.d index afc2133106df..7778166d6b54 100644 --- a/compiler/src/dmd/backend/dwarfdbginf.d +++ b/compiler/src/dmd/backend/dwarfdbginf.d @@ -2214,6 +2214,64 @@ static if (1) /* ======================= Type Index ============================== */ + /* Emit DW_TAG_enumeration_type with DW_TAG_enumerator children for the given enum symbol. + * Params: + * s = enum Classsym with SEenumlist populated + * base_type = the integral base type determining size and signedness + * Returns: DWARF type index of the emitted DW_TAG_enumeration_type + */ + uint dwarf_enum_typidx(Symbol* s, type* base_type) + { + if (s.Stypidx) + return s.Stypidx; + + uint sz = cast(uint)type_size(base_type); + + uint code = DWARFAbbrev.write!([ + DW_TAG_enumeration_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_data1, + ]); + + OutBuffer abuf; + abuf.writeByte(DW_TAG_enumerator); + abuf.writeByte(DW_CHILDREN_no); + abuf.writeByte(DW_AT_name); + abuf.writeByte(DW_FORM_string); + abuf.writeByte(DW_AT_const_value); + if (tyuns(base_type.Tty)) + abuf.writeByte(DW_FORM_udata); + else + abuf.writeByte(DW_FORM_sdata); + abuf.writeByte(0); + abuf.writeByte(0); + uint membercode = dwarf_abbrev_code(abuf.buf, abuf.length()); + + uint idx = cast(uint)debug_info.buf.length(); + debug_info.buf.writeuLEB128(code); + debug_info.buf.writeStringz(getSymName(s)); // DW_AT_name + debug_info.buf.writeByte(cast(ubyte)sz); // DW_AT_byte_size + + foreach (sl2; ListRange(s.Senum.SEenumlist)) + { + Symbol* sf = cast(Symbol*)list_ptr(sl2); + const value = cast(uint)el_tolong(sf.Svalue); + + debug_info.buf.writeuLEB128(membercode); + debug_info.buf.writeStringz(getSymName(sf)); // DW_AT_name + if (tyuns(base_type.Tty)) + debug_info.buf.writeuLEB128(value); + else + debug_info.buf.writesLEB128(value); + } + + debug_info.buf.writeByte(0); // no more children + + s.Stypidx = idx; + resetSyms.push(s); + return idx; + } + uint dwarf_typidx(type* t, Symbol* sym = null) { uint idx = 0; @@ -2340,6 +2398,10 @@ static if (1) } } + // D enum: stored as base type for ABI, but has enum members attached for debug info + if (t.Tflags & TF.denum) + return dwarf_enum_typidx(t.Ttag, t); + immutable tym_t ty = tybasic(t.Tty); // use cached basic type if it's not TYdarray or TYdelegate if (!(t.Tnext && (ty == TYdarray || ty == TYdelegate))) @@ -2988,8 +3050,6 @@ static if (1) Symbol* s = t.Ttag; enum_t* se = s.Senum; type* tbase2 = s.Stype.Tnext; - uint sz = cast(uint)type_size(tbase2); - symlist_t sl; if (s.Stypidx) return s.Stypidx; @@ -3008,50 +3068,7 @@ static if (1) break; // don't set Stypidx } - code = DWARFAbbrev.write!([ - DW_TAG_enumeration_type, DW_CHILDREN_yes, // child (the subrange type) - DW_AT_name, DW_FORM_string, - DW_AT_byte_size, DW_FORM_data1, - ]); - - uint membercode; - OutBuffer abuf; - abuf.writeByte(DW_TAG_enumerator); - abuf.writeByte(DW_CHILDREN_no); - abuf.writeByte(DW_AT_name); - abuf.writeByte(DW_FORM_string); - abuf.writeByte(DW_AT_const_value); - if (tyuns(tbase2.Tty)) - abuf.writeByte(DW_FORM_udata); - else - abuf.writeByte(DW_FORM_sdata); - abuf.writeByte(0); - abuf.writeByte(0); - membercode = dwarf_abbrev_code(abuf.buf, abuf.length()); - - idx = cast(uint)debug_info.buf.length(); - debug_info.buf.writeuLEB128(code); - debug_info.buf.writeStringz(getSymName(s)); // DW_AT_name - debug_info.buf.writeByte(cast(ubyte)sz); // DW_AT_byte_size - - foreach (sl2; ListRange(s.Senum.SEenumlist)) - { - Symbol* sf = cast(Symbol*)list_ptr(sl2); - const value = cast(uint)el_tolong(sf.Svalue); - - debug_info.buf.writeuLEB128(membercode); - debug_info.buf.writeStringz(getSymName(sf)); // DW_AT_name - if (tyuns(tbase2.Tty)) - debug_info.buf.writeuLEB128(value); - else - debug_info.buf.writesLEB128(value); - } - - debug_info.buf.writeByte(0); // no more children - - s.Stypidx = idx; - resetSyms.push(s); - return idx; // no need to cache it + return dwarf_enum_typidx(s, tbase2); } default: diff --git a/compiler/src/dmd/backend/type.d b/compiler/src/dmd/backend/type.d index 5325d3ac848d..7870fc57539c 100644 --- a/compiler/src/dmd/backend/type.d +++ b/compiler/src/dmd/backend/type.d @@ -55,6 +55,7 @@ enum TF : ushort static_ = 0x40, // TYarray: static dimension vla = 0x80, // TYarray: variable length array emptyexc = 0x100, // tyfunc(): empty exception specification + denum = 0x200, // D enum: base type for ABI but Ttag points to enum Classsym for debug info } public import dmd.backend.symbol : symbol_struct_addField, symbol_struct_addBitField, symbol_struct_hasBitFields, symbol_struct_addBaseClass; diff --git a/compiler/src/dmd/glue/toctype.d b/compiler/src/dmd/glue/toctype.d index 100218bb1082..5c53f69ee1c9 100644 --- a/compiler/src/dmd/glue/toctype.d +++ b/compiler/src/dmd/glue/toctype.d @@ -14,7 +14,12 @@ module dmd.glue.toctype; import core.stdc.stdio; import core.stdc.stdlib; -import dmd.backend.cc : Classsym, Symbol; +import dmd.backend.cc : Classsym, enum_t, Symbol; +import dmd.backend.cdef : SC; +import dmd.backend.dlist : list_append; +import dmd.backend.el : el_long; +import dmd.backend.mem : mem_calloc; +import dmd.backend.symbol : symbol_calloc, symbol_name; import dmd.backend.ty; import dmd.backend.type; @@ -253,9 +258,39 @@ type* Type_toCtype(Type t) (cast(type*)t.ctype).Tcount++; return cast(type*)t.ctype; } - else if (symMemtype.toBasetype().ty == Tint32) + else if (symMemtype.isIntegral()) { - t.ctype = type_enum(sym.toPrettyChars(true), Type_toCtype(symMemtype)); + type* basectype = Type_toCtype(symMemtype); + if (driverParams.symdebug && sym.members) + { + // Use the base type for ABI (avoids TYenum hardcoded-to-4-bytes issues), + // but attach a D enum Classsym via TF.denum so the DWARF backend can + // emit DW_TAG_enumeration_type with member names and values. + type* enumctype = type_alloc(tybasic(basectype.Tty)); + enumctype.Tcount++; + enumctype.Tflags = cast(TF)(basectype.Tflags | TF.denum); + import core.stdc.string : strlen; + const name = sym.toPrettyChars(true); + Symbol* s = symbol_calloc(name[0 .. strlen(name)]); + s.Senum = cast(enum_t*)mem_calloc(enum_t.sizeof); + s.Sclass = SC.enum_; + s.Stype = enumctype; + enumctype.Ttag = s; + foreach (m; *sym.members) + { + EnumMember em = m.isEnumMember(); + if (!em) + continue; + Symbol* sf = symbol_name(em.ident.toString(), SC.enum_, basectype); + sf.Svalue = el_long(totym(symMemtype.toBasetype()), em.value().toInteger()); + list_append(&s.Senum.SEenumlist, sf); + } + t.ctype = enumctype; + } + else + { + t.ctype = basectype; + } } else { @@ -270,12 +305,13 @@ type* Type_toCtype(Type t) // Copy mutable version of backend type and add modifiers type* mctype = Type_toCtype(t.castMod(0)); - if (tybasic(mctype.Tty) == TYenum) + if (mctype.Tflags & TF.denum) { Classsym* s = mctype.Ttag; assert(s); - type* tr = type_allocn(TYenum, mctype.Tnext); - tr.Ttag = s; // enum tag name + type* tr = type_alloc(tybasic(mctype.Tty)); + tr.Tflags = mctype.Tflags; + tr.Ttag = s; tr.Tcount++; tr.Tty |= modToTym(t.mod); return tr; diff --git a/compiler/test/dshell/extra-files/dwarf/excepted_results/issue20004.txt b/compiler/test/dshell/extra-files/dwarf/excepted_results/issue20004.txt new file mode 100644 index 000000000000..98c7607c19a3 --- /dev/null +++ b/compiler/test/dshell/extra-files/dwarf/excepted_results/issue20004.txt @@ -0,0 +1,21 @@ +DW_TAG_enumeration_type +DW_AT_name : issue20004.Color +DW_TAG_enumerator +DW_AT_name : red +DW_AT_const_value : 0 +DW_AT_name : green +DW_AT_const_value : 1 +DW_AT_name : blue +DW_AT_const_value : 2 +DW_AT_name : issue20004.Small +DW_AT_byte_size : 1 +DW_AT_name : a +DW_AT_const_value : 10 +DW_AT_name : b +DW_AT_const_value : 20 +DW_AT_name : issue20004.Big +DW_AT_byte_size : 8 +DW_AT_name : x +DW_AT_const_value : 1 +DW_AT_name : y +DW_AT_const_value : 1000000 diff --git a/compiler/test/dshell/extra-files/dwarf/issue20004.d b/compiler/test/dshell/extra-files/dwarf/issue20004.d new file mode 100644 index 000000000000..cc25c83973fa --- /dev/null +++ b/compiler/test/dshell/extra-files/dwarf/issue20004.d @@ -0,0 +1,8 @@ +enum Color { red, green, blue } +enum Small : ubyte { a = 10, b = 20 } +enum Big : ulong { x = 1, y = 1000000 } +void main() { + Color c = Color.red; + Small s = Small.a; + Big b = Big.y; +}