Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

Change Log: 2.112.0

previous version: 2.111.0

Download D 2.112.0
released Jan 07, 2026

2.112.0 comes with 22 major changes and 0 fixed Bugzilla issues. A huge thanks goes to the 73 contributors who made 2.112.0 possible.

List of all bug fixes and enhancements in D 2.112.0.

Compiler changes

  1. The compiler now lowers associative array operations to a templated implementation in druntime

    The compiler now lowers associative array operations to templates defined in core.internal.newaa instead of relying on a precompiled implementation based on TypeInfo.

    In addition to better performance by not having to go through TypeInfo and allowing inlining of simple operations, this paves the way for proper inference of function attributes inherited from key and value constructors, toHash() on the key and comparison of key values. This inference is currently mostly avoided to keep backward compatibility with the runtime implementation that doesn't check function attributes.

    Some changes that are the result of the refactoring:

    • AA support no longer available at CTFE without object.d/newaa.d. Some operations used to work, but now need the lowerings before these can be intercepted by the interpreter.

    • creating an AA literal at runtime passes keys and values as arguments normally, not with special move/blit operations, so more postblit/destructor operations can happen

    • _d_assocarrayliteralTXTrace removed. Apart from the creation of associative array literals no other AA operation is hooked for -profile-gc. You will now see the allocations as done by the AA implementation.

    • aa[key] = S() with S.__ctor and S.opAssign is no longer a double lookup

    • aa.remove(key) now works with alias this

  2. Keywords auto and ref must be adjacent for auto ref return.

    Similar to auto ref parameters in 2.111, it's now deprecated to declare an auto ref return type without putting those two keywords next to each other as well.

    ref auto int f() => 3;
    auto { ref int g() => 3; }
    
    // Correction:
    auto ref f() => 3;
    auto ref g() => 3;
    
  3. Bitfields Are Now Incorporated

    There is no longer a requirement to throw the switch -preview=bitfields.

  4. An error is now issued for dangling else statements

    This used to give a warning when compiled with -w and now gives an error:

    int i, j;
    if (i)
        if (j)
            return 1;
        else    // Error: else is dangling, add { } after condition `if (i)`
            return 2;
    
  5. The compiler now accepts -extern-std=c++23

    The compiler now accepts c++23 as a supported standard for -extern-std=. Currently this only changes the value of __traits(getTargetInfo, "cppStd").

  6. External import path switch

    A new switch is added, the external import path (-extI). It is similar to the import path switch (-I) except it indicates that a module found from it is external to the currently compiling binary.

    It is used on Windows when the dllimport override switch is set to anything other than none to force an external module's symbols as DllImport.

    If a build system supports the external import path switch, it is recommend to not use the all option for the dllimport override switch which applies to symbols that should not be getting DllImport'd.

  7. C files can now include a module statement

    Similar to the __import extension, the __module keyword brings D's module declaration to C.

    It's particularly useful when you want to import multiple C files with the same name (e.g. hello/utils.c and world/utils.c), since both have to be imported with import utils when they are listed on the command line, resulting in conflicts.

    Now you can do:

    hello/utils.c:

    #if __IMPORTC__
    __module hello.utils;
    #endif
    
    int sqr(int x) { return x * x; }
    

    world/utils.c:

    #if __IMPORTC__
    __module world.utils;
    #endif
    
    int max(int a, int b) { return a > b ? a : b; }
    

    app.d:

    import hello.utils;
    import world.utils;
    
    static assert(sqr(3) == 9);
    static assert(max(3, 5) == 5);
    

    A __module declaration can appear anywhere in the top level scope. When there are multiple, the first one will be used. Therefore, every #include containing a __module declaration should come after the file's own module declaration, or it will be overwritten. When you always put the __module declaration at the very top like in D, there won't be such problems.

  8. Implicit integer conversions in int op= float assignments has been deprecated

    This is to prevent potential mistakes when op= assignment would implicitly truncate the right hand side expression from a non-zero value to zero.

    uint a;
    float b = 0.1;
    a += b; // Deprecation: `uint += float` is performing truncating conversion
    

    The corrective action if truncating was intentional is to explicitly cast the floating point expression to integer.

    a += cast(uint) b;
    

Runtime changes

  1. Templatized _d_arraysetlengthT to remove TypeInfo dependency

    The internal runtime function _d_arraysetlengthT was templatized to operate directly on the type T, removing its dependency on TypeInfo. This improves type safety, reduces runtime reflection, and allows the compiler to generate specialized code paths for different array element types.

    This change preserves the semantics of .length assignment on dynamic arrays, ensuring memory allocation, element initialization, and postblit handling continue to work as expected.

    /**
    Resize a dynamic array by setting its `.length` property.
    
    New elements are initialized according to their type:
    - Zero-initialized if applicable
    - Default-initialized via `emplace`
    - Or `memcpy` if trivially copyable
    */
    size_t _d_arraysetlengthT(Tarr : T[], T)(return ref scope Tarr arr, size_t newlength);
    
    int[] a = [1, 2];
    a.length = 3; // becomes _d_arraysetlengthT!(int)(a, 3)
    

    This reduces runtime dependency on TypeInfo, making the function more predictable and performant.

    See also: PR #21151

  2. Templatize _d_arrayappendcTX runtime hook

    This refactorization discards the TypeInfo parameter, replacing it with a template type parameter.

  3. Templatize _d_arraysetcapacity runtime hook

    This refactorization discards the TypeInfo parameter, replacing it with a template type parameter.

  4. core.int128: Add mul and udivmod overloads for 64-bit operands

    These map to a single x86_64 instruction and have accordingly been optimized via inline assembly.

    import core.int128;
    
    ulong a, b;
    Cent product128 = mul(a, b);
    
    ulong divisor64 = …;
    ulong modulus64;
    ulong quotient64 = udivmod(product128, divisor64, modulus64);
    
  5. Fixed generated binaries crashing on macOS 15.4

    macOS 15.4 has introduced an undocumented ABI change to the format of thread local variable section, which causes almost all executable built with previous D compiler versions to crash during initialization, if they use DRuntime. This release introduces a mitigation for this issue that is backwards compatible with previous versions of macOS.

  6. C Macro translations in druntime have been translated to templates

    This prevents linking errors when using -betterC. For example:

    import core.sys.posix.stdlib;
    import core.sys.posix.unistd;
    
    extern(C) int main()
    {
        int status, pid = vfork();
        if (pid == 0)
        {
            // ...
            return 0;
        }
    
        waitpid(pid, &status, 0);
        if (WIFEXITED(status))
        {
            // ...
        }
        return 0;
    }
    

    This would fail to compile with the -betterC flag:

    Error: undefined reference to `core.sys.posix.sys.wait.WIFEXITED(int)`
           referenced from `main`
    

    The reason is that WIFEXITED is a C macro that was translated to a D function in druntime, which requires linking with druntime to use. Now that it's a template, it will be lazily instantiated and the program compiles.

Library changes

  1. Add lazyCache to std.algorithm.iteration

    The new lazyCache function provides a lazily evaluated range caching mechanism. Unlike cache, which eagerly evaluates range elements during construction, lazyCache defers evaluation until elements are explicitly requested.

    auto result = iota(-4, 5).map!(a => tuple(a, expensiveComputation(a)))().lazyCache();
    // No computations performed at this point
    
    auto firstElement = result.front;
    // First element is now evaluated
    

    See the std.algorithm.iteration.lazyCache documentation for more details.

  2. getrandom() backwards compatibility shim

    To restore compatibility with older Linux platforms where getrandom() is unavailable either due to an outdated kernel or a legacy C library, Phobos now ships with a shim that emulates a limited subset of getrandom()’s behavior by reading random bytes from /dev/urandom.

    To enable the shim, build DMD and Phobos with the environment variable LINUX_LEGACY_EMULATE_GETRANDOM set to 1.

    cd phobos
    LINUX_LEGACY_EMULATE_GETRANDOM=1 make
    

    This functionality is a temporary fix and expected to be removed again soon by an upcoming release (approx. v2.112.0 or v2.113.0). The expected change is to replace the current “binding or shim” solution with a syscall wrapper and automatic /dev/urandom fallback.

  3. Add an internal multi-backend entropy system

    This Phobos release introduces an internal multi-backend system for the retrieval of entropy (as in cryptographically-secure random numbers obtained from a suitable random number generator provided by the operating system).

    The current implementation supports the getrandom syscall on Linux.

    On BSD systems arc4random_buf or getentropy are used — depending on which is implemented by the OS and powered by a secure (non-RC4) algorithm.

    Additionally, reading entropy from the character devices /dev/urandom and /dev/random is available on all POSIX targets.

    On Windows BCryptGenRandom (from the Cryptography API: Next Generation (“BCrypt”)) is provided as a backend. CryptGenRandom from the legacy CryptoAPI is not supported for the time being.

    Furthermore, this replaces the getrandom backwards compatibility shim that had been added by v2.111.1 for Linux targets. Instead backwards compatibility is now provided by a hunt strategy algorithm that tries potentially available entropy sources one by one to find one that is available on the running system. Given that the character devices serve as a fallback option here, urandom is favored over random. That is because modern kernel versions — where random would exhibit the usually more preferable behavior of blocking only until the entropy pool has been initialized — will also provide the getrandom syscall in the first place. Performing the syscall, in turn, is even better as it does not depend on the runtime environment exposing the special devices in predefined locations, thus working also within chroot environments.

  4. std.uni has been upgraded from Unicode 16.0.0 to 17.0.0

    This Unicode update was released September 9, 2025, and adds new blocks with characters. See: https://www.unicode.org/versions/Unicode17.0.0/

    import std;
    
    void main()
    {
        const alphaCount = iota(0, dchar.max).filter!(std.uni.isAlpha).walkLength;
        writeln(alphaCount);
        // formerly: 142759
        // now:      147421
    }
    
  5. Add uuid v7 support to std.uuid

    Add uuid v7 support to the UUID type located in std.uuid. The first 48 bits of v7 stores the milliseconds since the unix epoch (1970-01-01), additionally 74 bit are used to store random data.

    Example:

    SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
    UUID u = UUID(st);
    SysTime o = u.v7Timestamp();
    assert(o == st);
    
    string s = u.toString();
    UUID u2 = UUID(s);
    SysTime o2 = u2.v7Timestamp();
    assert(o2 == st);
    
  6. Add writeText, writeWText, and writeDText to std.conv

    These functions are variants of the existing text, wtext, and dtext functions. Instead of returning a string, they write their output to an output range.

    Like text, writeText can accept an interpolated expression sequence as an argument.

    Example:

    import std.conv : writeText;
    import std.array : appender;
    
    auto output = appender!string();
    output.writeText(i"2 + 2 == $(2 + 2)");
    assert(output.data == "2 + 2 == 4");
    

Dub changes

  1. Added --dest command line build option.

    Adds support for specifying a root directory for staging, essentially acts as a prefix to the targetPath and workingDirectory dubfile entries.

  2. Add frameworks dubfile key.

    Adds support for specifying macOS frameworks to link against, this replaces the need to manually specify frameworks via lflags or dflags.

    Before:

    lflags "-framework" "Cocoa"
    

    After:

    frameworks "Cocoa" "OpenGL"
    

List of all bug fixes and enhancements in D 2.112.0:

DMD Compiler bug fixes

  1. DMD Issue 17462: object.require modifies AA before populating value
  2. DMD Issue 17481: Memory leak in rt.minfo sortCtor
  3. DMD Issue 17804: not an associative array initializer
  4. DMD Issue 18018: goto across assignment to AA fails and reports false Error
  5. DMD Issue 18101: wrong code generated for bitfield assignment to ?:
  6. DMD Issue 18127: ImportC: redeclaration of struct in different translation unit doesn’t check compatibility
  7. DMD Issue 18247: Bitfield with 64 bits always zero
  8. DMD Issue 18263: ImportC fails when DMD is installed in path with spaces
  9. DMD Issue 18614: RTInfo generation can fail for structs defined in imported modules
  10. DMD Issue 18950: CodeView: debug info for return type of ref return function
  11. DMD Issue 19587: No debug line info for simple code blocks
  12. DMD Issue 19788: parameters inherit function UDAs
  13. DMD Issue 19983: ImportC function redeclarations should be allowed in function scope
  14. DMD Issue 20075: "none of the overloads of __ctor are callable using a immutable object" error message is backwards
  15. DMD Issue 20092: importC: can't take address of some compound-literals
  16. DMD Issue 20114: AA in operator does not work with static if
  17. DMD Issue 20157: ImportC: only 1 designator currently allowed for C struct field initializer
  18. DMD Issue 20184: ImportC recognizes .i and .c files, but not .h files
  19. DMD Issue 20301: "The CodeView record is corrupted" heisenbug
  20. DMD Issue 20318: Compiler should explain why implicit conversion to mutable fails
  21. DMD Issue 20334: ImportC: enums created from string literal #defines don’t implicitly convert to const(char)* in D.
  22. DMD Issue 20365: -preview=bitfields: Bit field address escapes through ref
  23. DMD Issue 20473: Struct with both bitfields and a slice or class instance ref doesn't compile.
  24. DMD Issue 20499: [ImportC] typedef struct with name as a pointer cannot be used with struct name
  25. DMD Issue 20502: importc: macro conflicts with struct of same name
  26. DMD Issue 20855: -v command line switch should show import sources
  27. DMD Issue 20901: dip1000: can escape stack pointer through indexed array literal
  28. DMD Issue 20917: @nogc check fails when there are both nested @nogc function and a closure inside __trait(compiles)
  29. DMD Issue 20985: runnable\exe1.c heisenbugs on Win64
  30. DMD Issue 21024: Optimize x^^0, x^^1 and x^^2 expressions
  31. DMD Issue 21033: DMD ICE: Can't link simple program with -profile=gc
  32. DMD Issue 21052: Missing @nogc check for enum variable initialization
  33. DMD Issue 21054: No location for array literal sliced from init symbol
  34. DMD Issue 21068: Hidden base class constructor call message suggests impossible alias
  35. DMD Issue 21098: Can't compile a module with a single import statement, while the imported module compiles fine
  36. DMD Issue 21105: Is-expressions should not generate unnecessary code for lowerings
  37. DMD Issue 21126: Crash before main() on macOS 15.4
  38. DMD Issue 21142: seg fault on floating point code
  39. DMD Issue 21150: ImportC: alignment value expected, not _Alignof
  40. DMD Issue 21153: [REG 2.111.0] Infinite loop in isAliasThisTuple
  41. DMD Issue 21161: placement new fails on default-init struct
  42. DMD Issue 21179: Failure to convert const(T) to T after type is used in cast()
  43. DMD Issue 21189: wrong/missing error line when source file isn't regular
  44. DMD Issue 21203: Placement new doesn't count as 'initialisation'
  45. DMD Issue 21207: enum AA as a function parameter default value causes dmd to crash
  46. DMD Issue 21210: ImportC: Initializing struct containing array with = {0} fails
  47. DMD Issue 21215: Wrong location for "not a member" error when constructing a struct literal
  48. DMD Issue 21225: ImportC: macro interpreted as an enum conflicts with function
  49. DMD Issue 21241: ImportC: wrong static function called after linking when static functions in different C imports have same name
  50. DMD Issue 21247: DMD hangs in CTFE
  51. DMD Issue 21258: Phobos 3 build broken due to recent changes
  52. DMD Issue 21259: Wrong 'not an lvalue' error when assigning to sequence
  53. DMD Issue 21267: ImportC: __forceinline ignored
  54. DMD Issue 21271: ImportC: incorrect compatibility macro for __pragma
  55. DMD Issue 21280: Add __NR_getrandom
  56. DMD Issue 21298: [iasmgcc] Missing string literal for instruction template not always diagnosed
  57. DMD Issue 21303: struct destructor not called when sizeof == 0
  58. DMD Issue 21304: Redundant "cannot use array to initialize _error_" message when initializing invalid array
  59. DMD Issue 21317: Wrong Location for error messages
  60. DMD Issue 21382: CodeView: final class functions are marked as virtual even though they are not in the vtbl
  61. DMD Issue 21384: CodeView: function arguments are shown in the wrong order in a debugger
  62. DMD Issue 21403: Incorrect function contract syntax segfaults the compiler.
  63. DMD Issue 21406: [Regression 2.111] generating header with debug condition crashes compiler
  64. DMD Issue 21408: Array comparison with recursive inferred opEquals gives confusing error message
  65. DMD Issue 21414: __rvalue must be @system
  66. DMD Issue 21416: __rvalue ignored on call to ref function
  67. DMD Issue 21426: Windows GC only wakes up one thread when doing parallel scanning
  68. DMD Issue 21429: mixin templates cannot produce opDispatch or opBinary overloads with multiple mixins
  69. DMD Issue 21435: Bad codegen when building dmd with optimizations
  70. DMD Issue 21452: substInout converts to enum base type if it is an array
  71. DMD Issue 21456: foreach over associative array allows incompatible types for key and value
  72. DMD Issue 21476: Presence of move constructor causes overload error
  73. DMD Issue 21478: Ref parameter overload not called when struct implements copy constructor
  74. DMD Issue 21479: Unable to compile cast expression with -inline flag
  75. DMD Issue 21504: __traits(compiles) affects inference of @nogc in functions
  76. DMD Issue 21523: A template function in an imported module does not inline functions annotated with pragma(inline, true)
  77. DMD Issue 21549: Compiler crashing, maybe codegen issue...
  78. DMD Issue 21562: Trying to fill a void[] causes dmd to segfault
  79. DMD Issue 21576: "need 'this' to access member" in shorthand constructor
  80. DMD Issue 21605: Bad test: bug19652
  81. DMD Issue 21615: [Regression 2.110] Array length assignment does not extend into subsequent pages
  82. DMD Issue 21619: ICE core.exception.AssertError@src/dmd/pragmasem.d(456): Assertion failure
  83. DMD Issue 21630: enum and alias ignored on runtime foreach loop variables
  84. DMD Issue 21646: [REG2.112] Compile-error regression with disabled opCast and -checkaction=context
  85. DMD Issue 21660: Error overlapping initialization for D bit-fields
  86. DMD Issue 21663: Bit-fields missing from DWARF debug info
  87. DMD Issue 21665: Incorrect CodeView debug info for bitfields
  88. DMD Issue 21679: ImportC: Handle C code tokens in GCC IASM
  89. DMD Issue 21690: ICE when statically initializing a multidimensional AA
  90. DMD Issue 21693: __rvalue ignored with component expressions
  91. DMD Issue 21718: importC: _Alignas specifiers cannot reduce alignment of variables
  92. DMD Issue 21740: ImportC: UCN accepts invalid characters as part of the symbol name
  93. DMD Issue 21744: ImportC: four-digit UCN identifiers \unnnn don't match eight-digit \Unnnnnnnn
  94. DMD Issue 21745: ImportC: __func__ is not generated if referenced inside a __check() expression
  95. DMD Issue 21757: MS Linker may fail on 32 bit debug builds: stack overflow or LNK1318 fatal error.
  96. DMD Issue 21774: resolveAddressesWithAtos leaks PIDs and leads to file descriptor exhaustion
  97. DMD Issue 21813: ImportC dies on _declspec(deprecated)
  98. DMD Issue 21874: Calling typesafe variadic function of primitive or struct non-array type with no arguments will give compile error: "Error: unkown, [...]"
  99. DMD Issue 21945: [REG] DIP1000 scope array of arrays comparison results in weird error
  100. DMD Issue 22323: [Regression master] error "cannot get frame pointer" with AA in struct literal

Phobos bug fixes

  1. Phobos Issue 9829: [optimization]: std.conv.parse!(int, string) counts when doCount is false
  2. Phobos Issue 10440: Template constraint on each is too wide
  3. Phobos Issue 10491: Complex!float.abs / hypot invalid result when argument is small
  4. Phobos Issue 10506: std.string.wrap has no policy on how it counts columns
  5. Phobos Issue 10513: The current implementation of powmod is very slow for the ulong type
  6. Phobos Issue 10540: rename the variable "degrees" in sumtype.d at least in the kelvin case
  7. Phobos Issue 10550: formattedWrite should support string interpolation
  8. Phobos Issue 10577: [std.math] std.math.algebraic.cbrt is not pure
  9. Phobos Issue 10701: Broken link in documentation of randomUUID
  10. Phobos Issue 10731: Windows link error with std.random.uniform() in v2.111
  11. Phobos Issue 10742: std.path.withExtension does not work for ranges incompatible with const
  12. Phobos Issue 10775: Respect TZDIR
  13. Phobos Issue 10801: sgnGamma(-0.5) should be -1
  14. Phobos Issue 10802: digamma handling of zero inconsistent with how gamma handles it
  15. Phobos Issue 10811: [REG2.111.0] RedBlackTree compile error for structs with move constructor
  16. Phobos Issue 10840: writeln doesn't support printing bit-fields

dlang.org bug fixes

  1. Dlang.org Issue 4184: The spec should warn against using copy constructors, not warn against using postblit constructors
  2. Dlang.org Issue 4191: Warn against using mixin(__FUNCTION__)
  3. Dlang.org Issue 4217: [spec/expression] wrongly says EqualExpression doesn't handle null for classes
  4. Dlang.org Issue 4222: Comparison docs have wrong wording
  5. Dlang.org Issue 4224: [spec/expression] wrongly says CmpExpression doesn't handle null for classes
  6. Dlang.org Issue 4234: Document that calling a method on a struct rvalue is allowed
  7. Dlang.org Issue 4251: Associative array foreach key type can be ref when type is const-convertible
  8. Dlang.org Issue 4260: Types spec should have a section for void
  9. Dlang.org Issue 4266: "report a spec bug" still links to bugzilla
  10. Dlang.org Issue 4267: Clarify the two ct foreach statements
  11. Dlang.org Issue 4305: The shop is hidden

Contributors to this release (71)

A huge thanks goes to all the awesome people who made this release possible.

previous version: 2.111.0