Repository: dlang/dmd
Pull request #21388: make bitfields-mixin displayable in a debugger again
Author: login: rainers
Created: 2025-05-17T09:49:07Z | Merged: 2025-05-18T01:28:53Z

It's rather difficult to make sense of the raw bitfields-value in a debugger. With this change version Debugger_friendly allows to switch back to field-wise display at the cost of using a bit more memory.

This also prepares for using actual bitfields, but compilation fails because hashOf cannot take the address of the fields in that case.


Diff:
diff --git a/compiler/src/dmd/common/bitfields.d b/compiler/src/dmd/common/bitfields.d
index 72cb3e7d91c8..d9cade403455 100644
--- a/compiler/src/dmd/common/bitfields.d
+++ b/compiler/src/dmd/common/bitfields.d
@@ -10,6 +10,10 @@
  */
 module dmd.common.bitfields;
 
+//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
+version(Has_Bitfields)
+    version = Debugger_friendly; // without Has_Bitfields, this uses more space by using S
+
 /**
  * Generate code for bit fields inside a struct/class body
  * Params:
@@ -17,7 +21,7 @@ module dmd.common.bitfields;
  *   T = type of bit fields variable, must have enough bits to store all booleans
  * Returns: D code with a bit fields variable and getter / setter functions
  */
-extern (D) string generateBitFields(S, T)()
+extern (D) string generateBitFields(S, T, int ID = __LINE__)()
 if (__traits(isUnsigned, T))
 {
     import core.bitop: bsr;
@@ -55,22 +59,57 @@ if (__traits(isUnsigned, T))
     static assert(bitInfo.totalSize <= T.sizeof * 8,
         "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`");
 
+    version(Debugger_friendly)
+    {
+        // unique name needed to allow same name as in base class using `alias`, but without overloading
+        string bitfieldsName = "bitfields" ~ toString!(ID);
+        string bitfieldsRead = T.stringof~" "~bitfieldsName~"() const pure { return 0";
+        string bitfieldsWrite = "void "~bitfieldsName~"("~T.stringof~" v) {\n";
+    }
+
     foreach (size_t i, mem; __traits(allMembers, S))
     {
         enum typeName = typeof(__traits(getMember, S, mem)).stringof;
         enum shift = toString!(bitInfo.offset[i]);
         enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc.
-        result ~= "
-        "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); }
-        "~typeName~" "~mem~"("~typeName~" v) scope
+        version(Debugger_friendly)
+        {
+            string memacc = mem;
+            bitfieldsRead ~= "\n| (cast("~T.stringof~")("~memacc~" & "~sizeMask~") << "~shift~")";
+            bitfieldsWrite ~= memacc~" = cast("~typeName~")((v >> "~shift~") & "~sizeMask~");\n";
+            result ~= typeName~" "~mem;
+            version(Has_Bitfields)
+                result ~= " : "~toString!(bitInfo.size[i]);
+            enum meminit = __traits(getMember, S.init, mem);
+            result ~= " = "~meminit.stringof~";\n";
+        }
+        else
         {
-            bitFields &= ~("~sizeMask~" << "~shift~");
-            bitFields |= v << "~shift~";
-            return v;
-        }";
+            result ~= "
+                "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); }
+            "~typeName~" "~mem~"("~typeName~" v) scope
+            {
+                bitFields &= ~("~sizeMask~" << "~shift~");
+                bitFields |= v << "~shift~";
+                return v;
+            }";
+        }
+    }
+    version(Debugger_friendly)
+    {
+        bitfieldsRead ~= ";\n}\n";
+        bitfieldsWrite ~= "}\n";
+        result ~= "alias bitFields = "~bitfieldsName~";\n";
+        result ~= bitfieldsRead ~ bitfieldsWrite;
+        result ~= "\n}\n";
+        return result;
+    }
+    else
+    {
+        result ~= "\n}\n";
+        enum TP initVal = bitInfo.initialValue;
+        return result ~ " private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n";
     }
-    enum TP initVal = bitInfo.initialValue;
-    return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n";
 }
 
 ///
diff --git a/compiler/src/dmd/cparse.d b/compiler/src/dmd/cparse.d
index a0b7565bab8f..b3df87d286b2 100644
--- a/compiler/src/dmd/cparse.d
+++ b/compiler/src/dmd/cparse.d
@@ -6041,7 +6041,7 @@ final class CParser(AST) : Parser!AST
         {
             //printf("addSym() %s\n", s.toChars());
             if (auto v = s.isVarDeclaration())
-                v.isCmacro(true);       // mark it as coming from a C #define
+                v.isCmacro = true;       // mark it as coming from a C #define
             if (auto td = s.isTemplateDeclaration())
                 td.isCmacro = true; // mark as coming from a C #define
             /* If it's already defined, replace the earlier
diff --git a/compiler/src/dmd/dinterpret.d b/compiler/src/dmd/dinterpret.d
index 543c0e22e39f..ec96e53e5020 100644
--- a/compiler/src/dmd/dinterpret.d
+++ b/compiler/src/dmd/dinterpret.d
@@ -569,7 +569,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
     istatex.caller = istate;
     istatex.fd = fd;
 
-    if (fd.hasDualContext())
+    if (fd.hasDualContext)
     {
         Expression arg0 = thisarg;
         if (arg0 && arg0.type.ty == Tstruct)
@@ -722,7 +722,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
 
     if (tf.isRef && e.op == EXP.variable && e.isVarExp().var == fd.vthis)
         e = thisarg;
-    if (tf.isRef && fd.hasDualContext() && e.op == EXP.index)
+    if (tf.isRef && fd.hasDualContext && e.op == EXP.index)
     {
         auto ie = e.isIndexExp();
         auto pe = ie.e1.isPtrExp();
@@ -1745,7 +1745,7 @@ public:
             if (istate && istate.fd.vthis)
             {
                 result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis);
-                if (istate.fd.hasDualContext())
+                if (istate.fd.hasDualContext)
                 {
                     result = ctfeEmplaceExp!PtrExp(e.loc, result);
                     result.type = Type.tvoidptr.sarrayOf(2);
@@ -1761,7 +1761,7 @@ public:
         result = ctfeGlobals.stack.getThis();
         if (result)
         {
-            if (istate && istate.fd.hasDualContext())
+            if (istate && istate.fd.hasDualContext)
             {
                 assert(result.op == EXP.address);
                 result = result.isAddrExp().e1;
@@ -4933,7 +4933,7 @@ public:
             }
         }
 
-        if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors())
+        if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors)
         {
             error(e.loc, "CTFE failed because of previous errors in `%s`", fd.toChars());
             result = CTFEExp.cantexp;
diff --git a/compiler/src/dmd/dsymbol.d b/compiler/src/dmd/dsymbol.d
index e65068c7622b..5eb395cef71f 100644
--- a/compiler/src/dmd/dsymbol.d
+++ b/compiler/src/dmd/dsymbol.d
@@ -637,7 +637,7 @@ extern (C++) class Dsymbol : ASTNode
         static bool has2This(Dsymbol s)
         {
             if (auto f = s.isFuncDeclaration())
-                return f.hasDualContext();
+                return f.hasDualContext;
             if (auto ad = s.isAggregateDeclaration())
                 return ad.vthis2 !is null;
             return false;
diff --git a/compiler/src/dmd/dtoh.d b/compiler/src/dmd/dtoh.d
index 3946c25c9f03..4cb7d1ac5dda 100644
--- a/compiler/src/dmd/dtoh.d
+++ b/compiler/src/dmd/dtoh.d
@@ -895,7 +895,7 @@ public:
     {
         // Omit redundant declarations - the slot was already
         // reserved in the base class
-        if (fd.isVirtual() && fd.isIntroducing())
+        if (fd.isVirtual() && fd.isIntroducing)
         {
             // Hide placeholders because they are not ABI compatible
             writeProtection(AST.Visibility.Kind.private_);
@@ -1164,7 +1164,7 @@ public:
 
         auto fd = ad.aliassym.isFuncDeclaration();
 
-        if (fd && (fd.isGenerated() || fd.isDtorDeclaration()))
+        if (fd && (fd.isGenerated || fd.isDtorDeclaration()))
         {
             // Ignore. It's taken care of while visiting FuncDeclaration
             return;
@@ -1197,7 +1197,7 @@ public:
                 // Print prefix of the base class if this function originates from a superclass
                 // because alias might be resolved through multiple classes, e.g.
                 // e.g. for alias visit = typeof(super).visit in the visitors
-                if (!fd.isIntroducing())
+                if (!fd.isIntroducing)
                     printPrefix(ad.toParent().isClassDeclaration().baseClass);
                 else
                     printPrefix(pd);
diff --git a/compiler/src/dmd/e2ir.d b/compiler/src/dmd/e2ir.d
index ef025b31c048..96e846a60e8c 100644
--- a/compiler/src/dmd/e2ir.d
+++ b/compiler/src/dmd/e2ir.d
@@ -628,7 +628,7 @@ elem* toElem(Expression e, ref IRState irs)
         if (se.var.toParent2())
             fd = se.var.toParent2().isFuncDeclaration();
 
-        const bool nrvo = fd && (fd.isNRVO() && fd.nrvo_var == se.var || se.var.nrvo && fd.shidden);
+        const bool nrvo = fd && (fd.isNRVO && fd.nrvo_var == se.var || se.var.nrvo && fd.shidden);
         if (nrvo)
             s = fd.shidden;
 
@@ -7157,7 +7157,7 @@ elem* genHalt(Loc loc)
 private
 elem* setEthis2(Loc loc, ref IRState irs, FuncDeclaration fd, elem* ethis2, ref elem* ethis, ref elem* eside)
 {
-    if (!fd.hasDualContext())
+    if (!fd.hasDualContext)
         return null;
 
     assert(ethis2 && ethis);
diff --git a/compiler/src/dmd/escape.d b/compiler/src/dmd/escape.d
index a0c5472375e5..d3473f691d55 100644
--- a/compiler/src/dmd/escape.d
+++ b/compiler/src/dmd/escape.d
@@ -1557,7 +1557,7 @@ void escapeExp(Expression e, ref scope EscapeByResults er, int deref)
     void visitThis(ThisExp e)
     {
         // Special case because `__this2` isn't `ref` internally
-        if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
+        if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext)
         {
             escapeByValue(e, er);
             return;
diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d
index cc98955126cf..999345ff7725 100644
--- a/compiler/src/dmd/expressionsem.d
+++ b/compiler/src/dmd/expressionsem.d
@@ -228,7 +228,7 @@ FuncDeclaration hasThis(Scope* sc)
         {
             return null;
         }
-        if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2()))
+        if (!fd.isNested() || fd.isThis() || (fd.hasDualContext && fd.isMember2()))
             break;
 
         Dsymbol parent = fd.parent;
@@ -245,7 +245,7 @@ FuncDeclaration hasThis(Scope* sc)
         fd = parent.isFuncDeclaration();
     }
 
-    if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2()))
+    if (!fd.isThis() && !(fd.hasDualContext && fd.isMember2()))
     {
         return null;
     }
@@ -1817,7 +1817,7 @@ L1:
     if (e1.op == EXP.this_)
     {
         FuncDeclaration f = hasThis(sc);
-        if (f && f.hasDualContext())
+        if (f && f.hasDualContext)
         {
             if (f.followInstantiationContext(ad))
             {
@@ -2000,7 +2000,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc,
             scope bool function(DtorDeclaration) check, const string checkName)
 {
     auto dd = f.isDtorDeclaration();
-    if (!dd || !dd.isGenerated())
+    if (!dd || !dd.isGenerated)
         return;
 
     // DtorDeclaration without parents should fail at an earlier stage
@@ -2017,7 +2017,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc,
     }
 
     dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
-                        dd.isGenerated() ? "generated " : "".ptr,
+                        dd.isGenerated ? "generated " : "".ptr,
                         ad.toChars,
                         cast(int) checkName.length, checkName.ptr);
 
@@ -2048,7 +2048,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc,
         {
             field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
 
-            if (fieldSd.dtor.isGenerated())
+            if (fieldSd.dtor.isGenerated)
                 fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName);
             else
                 fieldSd.dtor.loc.errorSupplemental("   %.*s `%s.~this` is declared here",
@@ -3754,8 +3754,8 @@ private bool functionParameters(Loc loc, Scope* sc,
             int offset;
             if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
             {
-                const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
-                const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
+                const(char)* s1 = tret.isNaked ? " mutable" : tret.modToChars();
+                const(char)* s2 = tthis.isNaked ? " mutable" : tthis.modToChars();
                 .error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2);
                 err = true;
             }
@@ -4901,7 +4901,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             {
                 return setError();
             }
-            if (!exp.placement.type.isNaked())
+            if (!exp.placement.type.isNaked)
             {
                 error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type));
                 return setError();
@@ -6206,7 +6206,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (sd.ctor)
                 {
                     auto ctor = sd.ctor.isCtorDeclaration();
-                    if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated())
+                    if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated)
                         sd.ctor = null;
                 }
 
@@ -6952,7 +6952,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // declare dual-context container
-        if (exp.f && exp.f.hasDualContext() && !sc.intypeof && sc.func)
+        if (exp.f && exp.f.hasDualContext && !sc.intypeof && sc.func)
         {
             // check access to second `this`
             if (AggregateDeclaration ad2 = exp.f.isMember2())
@@ -8459,7 +8459,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         result = e;
         // declare dual-context container
-        if (f.hasDualContext() && !sc.intypeof && sc.func)
+        if (f.hasDualContext && !sc.intypeof && sc.func)
         {
             // check access to second `this`
             if (AggregateDeclaration ad2 = f.isMember2())
@@ -16269,7 +16269,7 @@ Expression getThisSkipNestedFuncs(Loc loc, Scope* sc, Dsymbol s, AggregateDeclar
         {
             n++;
             e1 = new VarExp(loc, f.vthis);
-            if (f.hasDualContext())
+            if (f.hasDualContext)
             {
                 // (*__this)[i]
                 if (n > 1)
@@ -17127,7 +17127,7 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration
              * postblit. Print the first field that has
              * a disabled postblit.
              */
-            if (postblit.isGenerated())
+            if (postblit.isGenerated)
             {
                 auto sd = p.isStructDeclaration();
                 assert(sd);
@@ -17169,7 +17169,7 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration
     if (auto ctor = d.isCtorDeclaration())
     {
         //printf("checkDisabled() %s %s\n", ctor.toPrettyChars(), toChars(ctor.type));
-        if (ctor.isCpCtor && ctor.isGenerated())
+        if (ctor.isCpCtor && ctor.isGenerated)
         {
             .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars());
             return true;
diff --git a/compiler/src/dmd/func.d b/compiler/src/dmd/func.d
index 6a573810a4b9..7391ea53edf4 100644
--- a/compiler/src/dmd/func.d
+++ b/compiler/src/dmd/func.d
@@ -820,18 +820,18 @@ extern (C++) class FuncDeclaration : Declaration
     bool addPreInvariant()
     {
         auto ad = isThis();
-        return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked());
+        return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked);
     }
 
     bool addPostInvariant()
     {
         auto ad = isThis();
-        return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked());
+        return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked);
     }
 
     override const(char)* kind() const
     {
-        return this.isGenerated() ? "generated function" : "function";
+        return this.isGenerated ? "generated function" : "function";
     }
 
     /********************************************
diff --git a/compiler/src/dmd/funcsem.d b/compiler/src/dmd/funcsem.d
index 3b7b3097c93c..17c75c02f89e 100644
--- a/compiler/src/dmd/funcsem.d
+++ b/compiler/src/dmd/funcsem.d
@@ -103,7 +103,7 @@ public:
     override void visit(TryFinallyStatement s)
     {
         DtorExpStatement des;
-        if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
+        if (fd.isNRVO && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
             fd.nrvo_var == des.var)
         {
             if (!(global.params.useExceptions && ClassDeclaration.throwable))
@@ -224,7 +224,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
     AggregateDeclaration ad = funcdecl.isThis();
     // Don't nest structs b/c of generated methods which should not access the outer scopes.
     // https://issues.dlang.org/show_bug.cgi?id=16627
-    if (ad && !funcdecl.isGenerated())
+    if (ad && !funcdecl.isGenerated)
     {
         funcdecl.storage_class |= ad.storage_class & (STC.TYPECTOR | STC.synchronized_);
         ad.makeNested();
@@ -409,7 +409,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
 
         sc.linkage = funcdecl._linkage;
 
-        if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
+        if (!tf.isNaked && !(funcdecl.isThis() || funcdecl.isNested()))
         {
             import core.bitop : popcnt;
             auto mods = MODtoChars(tf.mod);
@@ -1366,7 +1366,7 @@ bool functionSemantic3(FuncDeclaration fd)
             return false;
     }
 
-    return !fd.errors && !fd.hasSemantic3Errors();
+    return !fd.errors && !fd.hasSemantic3Errors;
 }
 
 // called from semantic3
@@ -1557,7 +1557,7 @@ int findVtblIndex(FuncDeclaration fd, Dsymbol[] vtbl)
                              fdv.toPrettyChars(), fdv.type.toTypeFunction().parameterList.parametersTypeToChars(),
                               fd.toPrettyChars(),  fd.type.toTypeFunction().parameterList.parametersTypeToChars(), fd.type.modToChars());
 
-                const char* where = fd.type.isNaked() ? "parameters" : "type";
+                const char* where = fd.type.isNaked ? "parameters" : "type";
                 deprecationSupplemental(fd.loc, "Either remove `override`, or adjust the `const` qualifiers of the "
                                         ~ "overriding function %s", where);
             }
@@ -3163,7 +3163,7 @@ extern (D) void checkMain(FuncDeclaration fd)
 extern (D) bool checkNRVO(FuncDeclaration fd)
 {
     //printf("checkNRVO*() %s\n", fd.ident.toChars());
-    if (!fd.isNRVO() || fd.returns is null)
+    if (!fd.isNRVO || fd.returns is null)
         return false;
 
     auto tf = fd.type.toTypeFunction();
diff --git a/compiler/src/dmd/glue.d b/compiler/src/dmd/glue.d
index d24909c07668..e13f0085dcb6 100644
--- a/compiler/src/dmd/glue.d
+++ b/compiler/src/dmd/glue.d
@@ -529,7 +529,7 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
     if (fd.isVirtual() && (fd.fensure || fd.frequire))
         f.Fflags3 |= Ffakeeh;
 
-    if (fd.hasNoEH())
+    if (fd.hasNoEH)
         // Same as config.ehmethod==EH_NONE, but only for this function
         f.Fflags3 |= Feh_none;
 
@@ -636,7 +636,7 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
         __gshared uint hiddenparami;    // how many we've generated so far
 
         const(char)* name;
-        if (fd.isNRVO() && fd.nrvo_var)
+        if (fd.isNRVO && fd.nrvo_var)
             name = fd.nrvo_var.ident.toChars();
         else
         {
@@ -645,7 +645,7 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
         }
         shidden = symbol_name(name[0 .. strlen(name)], SC.parameter, thidden);
         shidden.Sflags |= SFLtrue | SFLfree;
-        if (fd.isNRVO() && fd.nrvo_var && fd.nrvo_var.nestedrefs.length)
+        if (fd.isNRVO && fd.nrvo_var && fd.nrvo_var.nestedrefs.length)
             type_setcv(&shidden.Stype, shidden.Stype.Tty | mTYvolatile);
         irs.shidden = shidden;
         fd.shidden = shidden;
@@ -822,7 +822,7 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
      * 2. impact on function inlining
      * 3. what to do when writing out .di files, or other pretty printing
      */
-    if (global.params.trace && !fd.isCMain() && !fd.isNaked() && !fd.hasInlineAsm)
+    if (global.params.trace && !fd.isCMain() && !fd.isNaked && !fd.hasInlineAsm)
     {
         /* The profiler requires TLS, and TLS may not be set up yet when C main()
          * gets control (i.e. OSX), leading to a crash.
diff --git a/compiler/src/dmd/importc.d b/compiler/src/dmd/importc.d
index 059de62b1f9c..b8aadbe33149 100644
--- a/compiler/src/dmd/importc.d
+++ b/compiler/src/dmd/importc.d
@@ -553,10 +553,10 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
     auto vd = s.isVarDeclaration(); // new declaration
     auto vd2 = s2.isVarDeclaration(); // existing declaration
 
-    if (vd && vd.isCmacro())
+    if (vd && vd.isCmacro)
         return s2;
 
-    assert(!(vd2 && vd2.isCmacro()));
+    assert(!(vd2 && vd2.isCmacro));
 
     if (vd && vd2)
     {
diff --git a/compiler/src/dmd/inline.d b/compiler/src/dmd/inline.d
index ce7a4786a8d3..3e521644800e 100644
--- a/compiler/src/dmd/inline.d
+++ b/compiler/src/dmd/inline.d
@@ -478,7 +478,7 @@ public:
             if (ids.fd && e.var == ids.fd.vthis)
             {
                 result = new VarExp(e.loc, ids.vthis);
-                if (ids.fd.hasDualContext())
+                if (ids.fd.hasDualContext)
                     result = new AddrExp(e.loc, result);
                 result.type = e.type;
                 return;
@@ -511,7 +511,7 @@ public:
                 assert(fdv);
                 result = new VarExp(e.loc, ids.vthis);
                 result.type = ids.vthis.type;
-                if (ids.fd.hasDualContext())
+                if (ids.fd.hasDualContext)
                 {
                     // &__this
                     result = new AddrExp(e.loc, result);
@@ -521,7 +521,7 @@ public:
                 {
                     auto f = s.isFuncDeclaration();
                     AggregateDeclaration ad;
-                    if (f && f.hasDualContext())
+                    if (f && f.hasDualContext)
                     {
                         if (f.hasNestedFrameRefs())
                         {
@@ -603,7 +603,7 @@ public:
                 return;
             }
             result = new VarExp(e.loc, ids.vthis);
-            if (ids.fd.hasDualContext())
+            if (ids.fd.hasDualContext)
             {
                 // __this[0]
                 result.type = ids.vthis.type;
@@ -623,7 +623,7 @@ public:
         {
             assert(ids.vthis);
             result = new VarExp(e.loc, ids.vthis);
-            if (ids.fd.hasDualContext())
+            if (ids.fd.hasDualContext)
             {
                 // __this[0]
                 result.type = ids.vthis.type;
@@ -1315,7 +1315,7 @@ public:
         if (e.op == EXP.construct && e.e2.op == EXP.call)
         {
             auto ce = e.e2.isCallExp();
-            if (ce.f && ce.f.isNRVO() && ce.f.nrvo_var) // NRVO
+            if (ce.f && ce.f.isNRVO && ce.f.nrvo_var) // NRVO
             {
                 if (auto ve = e.e1.isVarExp())
                 {
@@ -1630,7 +1630,7 @@ public:
             return;
         if (fd.isUnitTestDeclaration() && !global.params.useUnitTests || fd.inlineScanned)
             return;
-        if (fd.fbody && !fd.isNaked())
+        if (fd.fbody && !fd.isNaked)
         {
             while (1)
             {
@@ -2042,7 +2042,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren
     {
         Expression e0;
         ethis = Expression.extractLast(ethis, e0);
-        assert(vthis2 || !fd.hasDualContext());
+        assert(vthis2 || !fd.hasDualContext);
         if (vthis2)
         {
             // void*[2] __this = [ethis, this]
diff --git a/compiler/src/dmd/opover.d b/compiler/src/dmd/opover.d
index 7baaeaa7fcfa..6f6b7a95b353 100644
--- a/compiler/src/dmd/opover.d
+++ b/compiler/src/dmd/opover.d
@@ -1046,7 +1046,7 @@ private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s,
     if (s)
     {
         functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2), null);
-        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors))
             return ErrorExp.get();
     }
     FuncDeclaration lastf = m.lastf;
@@ -1054,7 +1054,7 @@ private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s,
     if (s_r)
     {
         functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1), null);
-        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors))
             return ErrorExp.get();
     }
     if (m.count > 1)
diff --git a/compiler/src/dmd/s2ir.d b/compiler/src/dmd/s2ir.d
index e7f3abede7e6..e5e91eebc2d5 100644
--- a/compiler/src/dmd/s2ir.d
+++ b/compiler/src/dmd/s2ir.d
@@ -624,7 +624,7 @@ void Statement_toIR(Statement s, ref IRState irs, StmtState* stmtstate)
             assert(e);
 
             if (writetohp ||
-                (func.isNRVO() && func.nrvo_var))
+                (func.isNRVO && func.nrvo_var))
             {
                 // Return value via hidden pointer passed as parameter
                 // Write exp; return shidden;
diff --git a/compiler/src/dmd/semantic3.d b/compiler/src/dmd/semantic3.d
index bafb2e7d8719..303b32874c31 100644
--- a/compiler/src/dmd/semantic3.d
+++ b/compiler/src/dmd/semantic3.d
@@ -402,7 +402,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
             // functions may be widely used by dmd-compiled projects.
             // It also gives more time for the implementation of dual-context
             // functions to be reworked as a frontend-only feature.
-            if (funcdecl.hasDualContext())
+            if (funcdecl.hasDualContext)
             {
                 .deprecation(funcdecl.loc, "%s `%s` function requires a dual-context, which is deprecated", funcdecl.kind, funcdecl.toPrettyChars);
                 if (auto ti = sc2.parent ? sc2.parent.isInstantiated() : null)
@@ -615,7 +615,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 if (!funcdecl.fbody)
                     funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements());
 
-                if (funcdecl.isNaked())
+                if (funcdecl.isNaked)
                 {
                     fpreinv = null;         // can't accommodate with no stack frame
                     fpostinv = null;
@@ -970,7 +970,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                             /* https://issues.dlang.org/show_bug.cgi?id=10789
                              * If NRVO is not possible, all returned lvalues should call their postblits.
                              */
-                            if (!funcdecl.isNRVO())
+                            if (!funcdecl.isNRVO)
                                 exp = doCopyOrMove(sc2, exp, f.next, true, true);
 
                             if (tret.hasPointers())
@@ -1255,7 +1255,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                             {
                                 // 'this' is the monitor
                                 vsync = new VarExp(funcdecl.loc, funcdecl.vthis);
-                                if (funcdecl.hasDualContext())
+                                if (funcdecl.hasDualContext)
                                 {
                                     vsync = new PtrExp(funcdecl.loc, vsync);
                                     vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0);
@@ -1298,7 +1298,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 }
             }
 
-            if (funcdecl.isNaked() && (funcdecl.fensures || funcdecl.frequires))
+            if (funcdecl.isNaked && (funcdecl.fensures || funcdecl.frequires))
                 .error(funcdecl.loc, "%s `%s` naked assembly functions with contracts are not supported", funcdecl.kind, funcdecl.toPrettyChars);
 
             sc2.ctorflow.callSuper = CSX.none;
diff --git a/compiler/src/dmd/statementsem.d b/compiler/src/dmd/statementsem.d
index 523ed7b39781..9e15fab7bf20 100644
--- a/compiler/src/dmd/statementsem.d
+++ b/compiler/src/dmd/statementsem.d
@@ -3743,7 +3743,7 @@ public bool throwSemantic(Loc loc, ref Expression exp, Scope* sc)
     exp = exp.checkGC(sc);
     if (exp.isErrorExp())
         return false;
-    if (!exp.type.isNaked())
+    if (!exp.type.isNaked)
     {
         // @@@DEPRECATED_2.112@@@
         // Deprecated in 2.102, change into an error & return false in 2.112
@@ -4104,7 +4104,7 @@ void catchSemantic(Catch c, Scope* sc)
         // reference .object.Throwable
         c.type = getThrowable();
     }
-    else if (!c.type.isNaked() && !c.type.isConst())
+    else if (!c.type.isNaked && !c.type.isConst())
     {
         // @@@DEPRECATED_2.115@@@
         // Deprecated in 2.105, change into an error & uncomment assign in 2.115
diff --git a/compiler/src/dmd/tocsym.d b/compiler/src/dmd/tocsym.d
index 71ecf387e086..ead7289c2411 100644
--- a/compiler/src/dmd/tocsym.d
+++ b/compiler/src/dmd/tocsym.d
@@ -149,7 +149,7 @@ Symbol* toSymbol(Dsymbol s)
             {
                 if (FuncDeclaration fd = vd.toParent2().isFuncDeclaration())
                 {
-                    if (fd.isNRVO() && fd.nrvo_var == vd)
+                    if (fd.isNRVO && fd.nrvo_var == vd)
                     {
                         buf.writestring("__nrvo_");
                         buf.writestring(id);
diff --git a/compiler/src/dmd/tocvdebug.d b/compiler/src/dmd/tocvdebug.d
index 543af1974309..af2400448fcd 100644
--- a/compiler/src/dmd/tocvdebug.d
+++ b/compiler/src/dmd/tocvdebug.d
@@ -1023,7 +1023,7 @@ private extern (C++) class CVMember : Visitor
             int count = 0;
             int mlen = 2;
             {
-                if (fd.isIntroducing())
+                if (fd.isIntroducing)
                     mlen += 4;
                 mlen += cgcv.sz_idx * 2;
                 count++;
@@ -1052,7 +1052,7 @@ private extern (C++) class CVMember : Visitor
                     attribute |= 2*4;
                 else if (fd.isVirtual())
                 {
-                    if (fd.isIntroducing())
+                    if (fd.isIntroducing)
                     {
                         if (fd.isAbstract())
                             attribute |= 6*4;
@@ -1074,7 +1074,7 @@ private extern (C++) class CVMember : Visitor
                 q += cgcv.sz_idx;
                 TOIDX(q, cv4_memfunctypidx(fd));
                 q += cgcv.sz_idx;
-                if (fd.isIntroducing())
+                if (fd.isIntroducing)
                 {
                     TOLONG(q, fd.vtblIndex * target.ptrsize);
                     q += 4;
diff --git a/compiler/src/dmd/toir.d b/compiler/src/dmd/toir.d
index c391de783969..e1f22b06043d 100644
--- a/compiler/src/dmd/toir.d
+++ b/compiler/src/dmd/toir.d
@@ -420,7 +420,7 @@ elem* getEthis(Loc loc, ref IRState irs, Dsymbol fd, Dsymbol fdp = null, Dsymbol
  */
 elem* fixEthis2(elem* ethis, FuncDeclaration fd, bool ctxt2 = false)
 {
-    if (fd && fd.hasDualContext())
+    if (fd && fd.hasDualContext)
     {
         if (ctxt2)
             ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, tysize(TYnptr)));
@@ -448,7 +448,7 @@ elem* setEthis(Loc loc, ref IRState irs, elem* ey, AggregateDeclaration ad, bool
     {
         ethis = getEthis(loc, irs, ad);
     }
-    else if (thisfd.vthis && !thisfd.hasDualContext() &&
+    else if (thisfd.vthis && !thisfd.hasDualContext &&
           (adp == thisfd.toParent2() ||
            (adp.isClassDeclaration() &&
             adp.isClassDeclaration().isBaseOf(thisfd.toParent2().isClassDeclaration(), &offset)
@@ -752,7 +752,7 @@ uint setClosureVarOffset(FuncDeclaration fd)
         /* Can't do nrvo if the variable is put in a closure, since
          * what the shidden points to may no longer exist.
          */
-        assert(!fd.isNRVO() || fd.nrvo_var != v);
+        assert(!fd.isNRVO || fd.nrvo_var != v);
     }
     return aggAlignment;
 }
diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d
index ffbdfed6eb37..0950d492a5d1 100644
--- a/compiler/src/dmd/typesem.d
+++ b/compiler/src/dmd/typesem.d
@@ -944,7 +944,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
     if (!f)
         return nocpctor();
 
-    if (f.isDisabled() && !f.isGenerated())
+    if (f.isDisabled() && !f.isGenerated)
     {
         /* https://issues.dlang.org/show_bug.cgi?id=24301
          * Copy constructor is explicitly disabled
@@ -967,7 +967,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
             bsafe ? "@safe ".ptr : nullptr,
             bnogc ? "nogc"  .ptr : nullptr);
     }
-    else if (f.isGenerated() && f.isDisabled())
+    else if (f.isGenerated && f.isDisabled())
     {
         /* https://issues.dlang.org/show_bug.cgi?id=23097
          * Compiler generated copy constructor failed.
@@ -2027,7 +2027,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
             // duplicate a part of StructDeclaration::semanticTypeInfoMembers
             //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash);
 
-            if (sd.xeq && sd.xeq.isGenerated() && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
+            if (sd.xeq && sd.xeq.isGenerated && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
             {
                 uint errors = global.startGagging();
                 sd.xeq.semantic3(sd.xeq._scope);
@@ -5743,7 +5743,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
                     Expression e1;
                     Type t;
                     /* returns: true to continue, false to return */
-                    if (f.hasDualContext())
+                    if (f.hasDualContext)
                     {
                         if (f.followInstantiationContext(ad))
                         {
@@ -6466,9 +6466,9 @@ Covariant covariant(Type src, Type t, STC* pstc = null, bool cppCovariant = fals
              */
             if (t1.linkage == LINK.cpp && cppCovariant)
             {
-                notcovariant |= tp1.isNaked() != tp2.isNaked();
+                notcovariant |= tp1.isNaked != tp2.isNaked;
                 if (auto tpn1 = tp1.nextOf())
-                    notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked();
+                    notcovariant |= tpn1.isNaked != tp2.nextOf().isNaked;
             }
         }
     }
@@ -6574,7 +6574,7 @@ Lcovariant:
     /* https://issues.dlang.org/show_bug.cgi?id=23135
      * extern(C++) mutable member functions are not covariant with const.
      */
-    if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked())
+    if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked != t2.isNaked)
         goto Lnotcovariant;
 
     /* Can convert mutable to const
@@ -6663,7 +6663,7 @@ STC parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarati
 
     /* If haven't inferred the return type yet, can't infer storage classes
      */
-    if (!tf.nextOf() || !tf.isNothrow())
+    if (!tf.nextOf() || !tf.isNothrow)
         return stc;
 
     tf.purityLevel();
diff --git a/compiler/src/vcbuild/dmd.vcxproj b/compiler/src/vcbuild/dmd.vcxproj
index d7b7dd8d89e3..985ccfe1a6d1 100644
--- a/compiler/src/vcbuild/dmd.vcxproj
+++ b/compiler/src/vcbuild/dmd.vcxproj
@@ -181,12 +181,13 @@
       <AdditionalOptions>$(ExternalLinkerOptions) %(AdditionalOptions)</AdditionalOptions>
     </Link>
     <DCompile>
-      <VersionIdentifiers>MARS</VersionIdentifiers>
+      <VersionIdentifiers>MARS;Debugger_friendly</VersionIdentifiers>
       <StringImportPaths>../dmd/res;$(OutDir)</StringImportPaths>
       <CRuntimeLibrary>MultiThreadedDebug</CRuntimeLibrary>
       <CompilationModel>Package</CompilationModel>
       <ImportPaths>..</ImportPaths>
       <DebugInfo>Mago</DebugInfo>
+      <EnableDebugMixin>true</EnableDebugMixin>
     </DCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">


Reviews:
user: login: thewilsonator
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-17T09:50:58Z
user: login: dkorpel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T01:39:51Z
user: login: rainers
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-18T06:06:56Z

Review comments:
diff_hunk: @@ -10,14 +10,18 @@
  */
 module dmd.common.bitfields;
 
+//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
path: compiler/src/dmd/common/bitfields.d
user: login: dkorpel
body: What is this version `Has_Bitfields`, is it documented somewhere?
created_at: 2025-05-18T01:39:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,14 +10,18 @@
  */
 module dmd.common.bitfields;
 
+//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
path: compiler/src/dmd/common/bitfields.d
user: login: rainers
body: `Has_Bitfields` is a potential version that could be defined instead of `Debugger_friendly` to use the builtin bitfields without the space penalty, but as noted, the current implementation of `hashOf` tries to take the address of the fields and causes the build to fail.
created_at: 2025-05-18T06:06:56Z
author_association: MEMBER

PR comments:
user: login: dlang-bot
created_at: 2025-05-17T09:49:10Z
body: Thanks for your pull request, **@<!-- -->rainers**!

### Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please [reference a Bugzilla issue](https://github.com/dlang-bots/dlang-bot#automated-references) or create a [manual changelog](https://github.com/dlang/dmd/blob/master/changelog).

### Testing this PR locally

If you don't have a [local development environment setup](https://wiki.dlang.org/Starting_as_a_Contributor), you can use [Digger](https://github.com/CyberShadow/Digger) to test this PR:

```sh
dub run digger -- build "master + dmd#21388"
```

author_association: CONTRIBUTOR
user: login: rainers
created_at: 2025-05-17T09:54:27Z
body: An example of a Debugger_friendly mixin looks like this:
```D
// expansion at ..\dmd\expression.d(312)
extern (C++) pure nothrow @nogc @safe final {bool parens;
bool rvalue;
bool gcPassDone;
alias bitFields = bitfields312u;
ubyte bitfields312u() const pure { return 0
| (cast(ubyte)(parens & 1u) << 0u)
| (cast(ubyte)(rvalue & 1u) << 1u)
| (cast(ubyte)(gcPassDone & 1u) << 2u);
}
void bitfields312u(ubyte v) {
parens = cast(bool)((v >> 0u) & 1u);
rvalue = cast(bool)((v >> 1u) & 1u);
gcPassDone = cast(bool)((v >> 2u) & 1u);
}
}
```
author_association: MEMBER
user: login: rainers
created_at: 2025-05-17T19:03:44Z
body: Figured out the problem: there is one element (isNRVO) in the bitfields that is not initialized to the types' default. Added the initializers.
author_association: MEMBER