Change Log: 2.113.0
Download D nightlies
To be released
This changelog has been automatically generated from all commits in master since the last release.
- The full-text messages are assembled from the changelog/ directories of the respective repositories: dmd, druntime, phobos, tools, dlang.org, installer, and dub.
- See the DLang-Bot documentation for details on referencing Bugzilla. The DAutoTest PR preview doesn't include the Bugzilla changelog.
- The pending changelog can be generated locally by setting up dlang.org and running the pending_changelog target:
make -f posix.mak pending_changelog
Compiler changes
- The compiler now lowers associative array operations to a templated implementation in druntime
- Keywords auto and ref must be adjacent for auto ref return.
- Bitfields Are Now Incorporated
- An error is now issued for dangling else statements
- Finally statements are no longer rewritten to a sequence if no Exception was thrown
- The compiler now accepts -extern-std=c++23
- External import path switch
- New experimental Data Flow Analysis Engine for nullability and truthiness
- C files can now include a module statement
- Allow some forms of printf calls to be treated as @safe
- Implicit integer conversions in int op= float assignments has been deprecated
- The compiler now inlines pragma(inline, true) functions in a separate pass
- Add support for with(auto x = expression())
Runtime changes
- Templatized _d_arraysetlengthT to remove TypeInfo dependency
- Templatize _d_arrayappendcTX runtime hook
- Templatize _d_arraysetcapacity runtime hook
- Gravedigger approach to Throwables escaping a thread entry point is now available
- core.int128: Add mul and udivmod overloads for 64-bit operands
- Fixed generated binaries crashing on macOS 15.4
- C Macro translations in druntime have been translated to templates
Library changes
Dub changes
List of all upcoming bug fixes and enhancements in D 2.113.0.
Compiler changes
- 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
- 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;
- Bitfields Are Now Incorporated
There is no longer a requirement to throw the switch -preview=bitfields.
- 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;
- Finally statements are no longer rewritten to a sequence if no Exception was thrown
Finally statements have been allowed to rewrite to sequences when no Exception was throwable from within the try body. This has been shown to be problematic as cleanup when an Error is thrown does not occur.
By default this has been reverted back to the pre-2018 behavior of not doing the rewrite.
To change this use the new switch -checkactionfinally=off to reenable the previous behavior.
This behavior can be observed by running the following code:
import core.stdc.stdio; void main() { try { callMe(); } finally { printf("exiting!\n"); } } void callMe() { throw new Error("hi there :)"); }
When the switch is set to on, "exiting!" will be printed.
The -betterC switch is unaffected by this change.
- 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").
- 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.
- New experimental Data Flow Analysis Engine for nullability and truthiness
A new experimental Data Flow Analysis (DFA) has been implemented under the preview flag -preview=fastdfa. The intent of the engine is to be both fast and free from false positives, if successful it may in the future be turned on by default.
No attributes have been implemented to date, before they are considered the engine itself must be both usable with the right tradeoffs and have desirable features. This has some side effects, it prevent separate compilation, function pointers, and cyclic functions from being analysable. These limitations are not supposed to prevent a successful compilation when in use.
The engine itself is variable centric with a strong focus on giving up on analysing a variable if things get too complex for it. This can result in messages that may not appear to make sense for where they are emitted, due to the way shortcutting of analysis works. As an example of loops:
void loopy() { int* ptr = new int; foreach (i; 0 .. 2) // Error: Variable `ptr` was required to be non-null and has become null { int val = *ptr; ptr = null; } }
If the engine is successful, the reporting mechanism would be replaced with a tracing state pass. This would offer for a function line by line explanation of how and why the engine thought something was true.
The engine has been tested on a 100k LOC defensively written codebase without any false positives. The performance is similar to DIP1000 and is not supposed to be noticeable.
- 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.
- Allow some forms of printf calls to be treated as @safe
printf function calls in general are unsafe. Most calls, however, are safe. This change examines the format string to printf and allows format strings that do not include s formats to be allowed in @safe code.
For example:
@safe void func(int i, char* s) { printf("i is %d\n", i); // allowed printf("s is %s\n", s); // unsafe, not allowed }
- 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;
- The compiler now inlines pragma(inline, true) functions in a separate pass
The compiler now considers pragma(inline, true) functions for inlining in a dedicated pass before trying to inline other eligible functions. This provides better control of inlining decisions, for example:
auto staticSquares(uint n)() { int[n] arr; static foreach(i; 0 .. n) arr[i] = (i + 1) ^^ 2; return arr; } pragma(inline, true) int thirtyThirty() { return staticSquares!30[$ - 1]; }
Previously, dmd -inline would first inline staticSquares() into thirtyThirty() and subsequently fail to inline thirtyThirty() into its callers. With the new implementation, the programmer can expect the compiler to replace calls to thirtyThirty() with staticSquares!30[$ - 1] before making other inlining decisions.
- Add support for with(auto x = expression())
Added support for using with statements with an expression initialiser as an AssignExpression, like if, while for and switch`. For backwards compatibility, with still also accepts a qualified expression without assignment to a variable as in with(immutable expression()).
Runtime changes
- 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
- Templatize _d_arrayappendcTX runtime hook
This refactorization discards the TypeInfo parameter, replacing it with a template type parameter.
- Templatize _d_arraysetcapacity runtime hook
This refactorization discards the TypeInfo parameter, replacing it with a template type parameter.
- Gravedigger approach to Throwables escaping a thread entry point is now available
A new method is added to ThreadBase enabling filtering of any Throwable's before it is handled by the thread abstraction. This may be used per-thread and globally, to log that an Error has occured or to exit the process.
A reasonable error handler that may be of use is:
import core.exception; void main() { filterThreadThrowableHandler = (ref Throwable t) { import core.stdc.stdio; import core.stdc.stdlib; if (auto e = cast(Error) t) { auto msg = e.message(); fprintf(stderr, "Thread death due to error: %.*s\n", cast(int)msg.length, msg.ptr); fflush(stderr); abort(); } }; }
For a per thread handler the following example may be what you want:
import core.thread; class MyThread : Thread { this( void function() fn, size_t sz = 0 ) @safe pure nothrow @nogc { super(fn, sz); } this( void delegate() dg, size_t sz = 0 ) @safe pure nothrow @nogc { super(dg, sz); } override void filterCaughtThrowable(ref Throwable t) @system nothrow { import core.stdc.stdio; import core.stdc.stdlib; if (auto e = cast(Error) t) { auto msg = e.message(); fprintf(stderr, "Thread death due to error: %.*s\n", cast(int)msg.length, msg.ptr); fflush(stderr); abort(); } super.filterCaughtThrowable(t); } }
- 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);
- 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.
- 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
- 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.
- 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.
- 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 }
- 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);
- 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
- 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.
- 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"
- Added --timeout option to dub dustmite command.
Adds a timeout (in seconds) for each oracle invocation, preventing the dustmite process from hanging indefinitely when the test command does not terminate. Requires the timeout command to be available (coreutils on Linux/macOS).
- Fix issue where cImportPaths wasn't working with dmd and ldc
dub was passing -I
instead of -P-I as is required by those compilers - dub.selections.json files are now looked up in parent directories too
In case the root package directory doesn't contain a dub.selections.json file, dub now looks in parent directories too and potentially uses the first (deepest) one it finds - if and only if that JSON file contains an optional new "inheritable": true flag.
This allows using a 'central' dub.selections.json file for a repository containing multiple dub projects, making it automatically apply to all builds in that source tree if located in the repository root directory (unless a local dub.selections.json overrides it).
Such an inherited selections file is never mutated when running dub for a nested project, i.e., changes are always saved to a local dub.selections.json file. E.g., when running dub upgrade for a nested project.
List of all bug fixes and enhancements in D 2.113.0:
Contributors to this release (79)
A huge thanks goes to all the awesome people who made this release possible.
- Aditya Singh
- AhmedMaged
- Albert24GG
- Anders Ronnbrant
- Andrei Alexandrescu
- Andrew Edwards
- Anton Oks
- apz28
- Aryan Dadwal
- Atila Neves
- Ayan Das
- Bastiaan Veelo
- Ben Jones
- blobbo
- Brad Anderson
- Brad Roberts
- Carlos Une
- Chris
- Claude Code
- Clouuday
- Clouudy
- dawg
- Dejan Lekic
- Denis Feklushkin
- Dennis
- Dennis Korpel
- eco
- Emmankoko
- Emmanuel Nyarko
- Eugene 'Vindex' Stulin
- Hiroki Noda
- Iain Buclaw
- Inkrementator
- Iskaban10
- Jacob Carlborg
- Johan Engelen
- John Colvin
- Jonathan M Davis
- Jordi Sayol
- Kai Meyer
- Leandro Lucarella
- limepoutine
- Lionello Lunesu
- Lio李歐
- Loren Burkholder
- Manuel Maier
- Martin Kinkelin
- Martin Nowak
- Mathias Lang
- Max H Haughton
- Max Haughton
- Mike Parker
- Mohamed El Shorbagy
- MoonlightSentinel
- naydef
- Nicholas Wilson
- Nick Sabalausky
- Nick Treleaven
- Paul Backus
- Per Nordlöw
- Petar Kirov
- Pranjal Kole
- ProgramGamer
- Quirin F. Schroll
- Rainer Schuetze
- Razvan Nitu
- Richard (Rikki) Andrew Cattermole
- RubyTheRoobster
- Sebastian Wilzbach
- Sergii K
- Sergii Kuzko
- Steven Schveighoffer
- Sönke Ludwig
- Tim Schendekehl
- Tony Edgin
- Tushar
- vindexbit
- Vladimir Panteleev
- Walter Bright