Change Log: 2.113.0
Download D 2.113.0 Beta
to be released Aug 05, 2026
Compiler changes
- Support for default values in C-style bitfields
- Destructors now run in a nothrow function when an Error unwinds through it
- New experimental Data Flow Analysis Engine for nullability and truthiness
- Fast DFA reports on uninitialized variable reads
- Improve -ftime-trace template instance detail
- Added __traits(needsDestruction, T)
- New trait __traits(isOverlapped, field) to detect overlapping fields
- An optional check for null dereference is added
- Allow certain pragma(printf) function calls to be treated as @safe
- Add support for static array length inference
- The compiler now inlines pragma(inline, true) functions in a separate pass
- -vgc now reports locations of nested functions that create closures
- Add support for with(auto x = expression())
Library changes
Dub changes
List of all bug fixes and enhancements in D 2.113.0.
Compiler changes
- Support for default values in C-style bitfields
D now supports specifying default values for C-style bitfields in structs and classes. This brings valid D code closer to behavior allowed in C++20 and allows for more concise initialization of bitfield members.
Value range checking is performed at compile-time to ensure the default value fits within the specified number of bits.
struct S { int a : 4 = 2; // OK int b : 2 = 5; // Error: bitfield initializer `5` does not fit in 2 bits uint c : 1 = 1; // OK bool d : 1 = 1; // OK }
- Destructors now run in a nothrow function when an Error unwinds through it
Destructors and scope(exit) blocks internally generate try-finally statements. Since 2018, DMD has optimized finally blocks in nothrow try bodies by rewriting them into a simple sequence, avoiding the overhead of exception unwinding. However, this optimization also skipped finally blocks when an Error was thrown, preventing expected cleanup from running.
This optimization has been reverted to the pre-2018 behavior: finally blocks always run, regardless of whether the try body can throw an Exception.
To reenable the optimization, use the new -nothrow-optimizations switch.
The following example demonstrates the fix - "exiting!" is now printed even though callMe is a nothrow function:
import core.stdc.stdio; void main() { S s2; callMe(); } struct S { ~this() { printf("exiting\n"); } } void callMe() nothrow { throw new Error("hi there :)"); }
The -betterC switch is unaffected by this change.
- 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.
- Fast DFA reports on uninitialized variable reads
The fast DFA engine has gained the ability to error when a variable can be proven to being in an uninitialized state, and is read.
void readFromUninit() @system { int val1 = void; int val2 = val1; // error int* ptr = &val1; int val3 = *ptr; // error }
As part of this it is now capable of looking through pointers and seeing properties of variables that are on the stack.
An application of this feature is the prevention of mathematical operators from accessing default initialized floating point types.
void checkFloatInit(bool condition) { float v; float t = v * 2; // error math op if (condition) v = 2; float u = v * 2; // no error }
It will not activate for other expressions, or statements. Only in a mathematical expression.
This was not originally part of the fast DFA engines scope, its continued existence depends upon community feedback as part of usage.
- Improve -ftime-trace template instance detail
The -ftime-trace profiling output now includes template argument types in the event name for template instance events, allowing users to distinguish between different instantiations of the same template (e.g. isArray!(int) instead of just isArray).
- Added __traits(needsDestruction, T)
True if T is a value type needing elaborate destruction. This includes structs with explicit ~this() destructors and/or compiler-generated destructors (caused by fields needing destruction), static arrays thereof, and enums with such base types.
class C { ~this(); } struct S { ~this(); } static assert(!__traits(needsDestruction, C)); static assert(__traits(needsDestruction, S)); static assert(!__traits(needsDestruction, S[0])); static assert(__traits(needsDestruction, S[1]));
- New trait __traits(isOverlapped, field) to detect overlapping fields
D now provides a compile-time trait to check whether a struct or class field overlaps with other fields in memory. This is useful for serialization libraries, code generators, and metaprogramming tasks that need to identify fields sharing the same memory location.
The trait takes a single field argument, returning true if the field's storage overlaps with other fields (typically because it is part of a union).
struct S { int a; union { int x; // overlaps with y float y; // overlaps with x } int b; } static assert(__traits(isOverlapped, S.x)); // true static assert(__traits(isOverlapped, S.y)); // true static assert(!__traits(isOverlapped, S.a)); // false - regular field static assert(!__traits(isOverlapped, S.b)); // false - regular field
The trait works with both anonymous and named unions:
union NamedUnion { int x; float y; } static assert(__traits(isOverlapped, NamedUnion.x)); // true static assert(__traits(isOverlapped, NamedUnion.y)); // true
This trait is particularly useful for:
- Serialization libraries that need to handle only one field from overlapping sets
- Understanding memory layout and field interaction
- Implementing correct destructors for types with overlapping fields
- Generic code that needs to reason about field storage semantics
The trait exposes DMD's internal overlap tracking (VarDeclaration.overlapped), providing a direct way to query this semantic property.
- An optional check for null dereference is added
A new check has been implemented that injects code to check a pointer for null before it is dereferenced.
It will be typically be used if you need a backtrace generated (when one is not automatically done), or if you want to catch and handle the Error as part of a scheduler.
This can be enabled by using -check=nullderef=on by default it is off. What happens may be customized by the -checkaction switch and by setting a new handler in core.exception.
Due to issues in dmd's backend, not all pointer dereferences are guaranteed to get a check.
- Allow certain pragma(printf) function calls to be treated as @safe
printf function calls in general are unsafe. Many calls, however, can be automatically checked for safety.
This change allows calling a pragma(printf) function from @safe code when:
- the callee is marked @safe or @trusted
- the format string passed is a literal
- no zero-terminated string format specifiers are used
Note: pragma(printf) already enforces type safety.
For example:
extern(C) pragma(printf) void printf(const char* format, ...) @trusted; @safe void func(int i, char* s) { printf("i is %d\n", i); // allowed printf("s is %s\n", s); // Error: call is not safe printf(s); // Error: call is not safe }
- Add support for static array length inference
Added support for using $ to automatically infer the length of a static array from its initializer. This allows for cleaner declarations without manually counting elements.
int[$] arr = [1, 2, 3]; // Length is inferred as 3
- 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.
- -vgc now reports locations of nested functions that create closures
When a GC-allocated closure was created, the -vgc switch would only point to the outer function that a closure was created for. For a big function, this still requires you to search where the nested function requiring the closure actually is. It now reports the closure function and variable location just like in error messages for @nogc.
auto closure() { int x; int bar() { return x; } return &bar; }
After:
vgc.d(1): vgc: using closure causes GC allocation
vgc.d(1): vgc: using closure causes GC allocation vgc.d(4): vgc: function bar closes over variable x vgc.d(3): vgc: x declared here
- 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
- 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); } }
Library changes
- variant: Support big structs with @disabled this()
std.variant didn't compile when the payload was a struct that was bigger than its internal buffer and had to be allocated on the heap if that struct @disabled this(). The new handling now always skips the constructor and just copies the directly to the allocated regions.
Dub changes
- 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).
List of all bug fixes and enhancements in D 2.113.0:
DMD Compiler bug fixes
- DMD Issue 17224: No way to get notified about D runtime termination.
- DMD Issue 17531: Using a custom pow function for ^^
- DMD Issue 18337: Coverage always report 0000000 for inlined function
- DMD Issue 19499: pragms(lib) and pragma(linkerDirective) can emit duplicate entries to the object
- DMD Issue 19675: fatal error LNK1179 on windows-x86_64-dmd with MSVC
- DMD Issue 19721: __traits(getMember) should not allow safe code to access private fields
- DMD Issue 19829: Appending to keys of an empty associative array "cannot be interpreted at compile time"
- DMD Issue 19905: Type list (tuple) not expanded in delegate during IFTI
- DMD Issue 20053: Line with spaces in cmdfile is treated as multiple arguments
- DMD Issue 20425: ImportC: Assignment to double complex fails when using ternary operator
- DMD Issue 20439: "Undefined reference to internal" when -c with SysTime.max in init
- DMD Issue 20578: dmd -inline segfault on windows, mac, linux
- DMD Issue 20997: Alias template parameter with type specialization rejects valid argument
- DMD Issue 21039: Zero-length static array with scalar initializer causes ICE
- DMD Issue 21194: Modifying global char[] initialized with dup of string literal causes segfault
- DMD Issue 21637: ICE in tuple comparison
- DMD Issue 21707: Segfault with -vcg-ast and foreach on a sequence
- DMD Issue 21839: ICE - Crash when using void[N].init
- DMD Issue 21976: [REG 2.112.0] Array comparison performance regression
- DMD Issue 22153: importc: cannot use forward declaration of static function when building a library
- DMD Issue 22229: scope should be inferred when assigning a scope variable to another variable
- DMD Issue 22282: Applying a UDA to a File-type variable causes DMD to crash
- DMD Issue 22363: Noreturn variable access is not always detected
- DMD Issue 22381: out-of-bounds access when masking ushort
- DMD Issue 22394: Error in lambda should show call line as well as definition line
- DMD Issue 22397: DMD 2.112.0 fails to compile an array variable initialization with an array literal and a UDA
- DMD Issue 22399: Low-level threading support on Windows DLLs are broken
- DMD Issue 22406: [REG 2.112] Error: function '_d_aaLen' is not callable using argument types '(shared(int[int]))'
- DMD Issue 22427: Premature removal of ternary of type noreturn
- DMD Issue 22430: noreturn evaluation in ConditionalExpression is ignored
- DMD Issue 22463: SOURCE_DATE_EPOCH parsing incorrectly affected by system timezone
- DMD Issue 22481: Inliner silently removes return in loops
- DMD Issue 22489: DMD miscompiles real types in the presence of aliasing
- DMD Issue 22504: [REG 2.112] druntime fails to build on powerpc64le-linux-gnu
- DMD Issue 22512: [REG 2.112] druntime fails to build on sparcv9-sun-solaris2.11
- DMD Issue 22517: [REG 2.106] Allocating an array of type void[] is marked as not containing pointers
- DMD Issue 22544: [REG 2.112] Cannot index AA in with block
- DMD Issue 22560: [REG master] Can supposedly index AA with mismatching key type
- DMD Issue 22594: [REG 2.101] TypeInfo_Class.m_flags wrong wrt. noPointers flag when the only pointers come from tuple members
- DMD Issue 22613: Consecutive initialization fails with postblits
- DMD Issue 22621: Anonymous struct inside anonymous union not considered overlapped
- DMD Issue 22647: dmd as library fails to link with dub test --coverage
- DMD Issue 22658: __traits(isCopyable) fails on static array of non-copyable struct
- DMD Issue 22659: ICE when assigning to a slice cast to static array type
- DMD Issue 22667: ImportC: generating .di turns void into _IO_lock_t
- DMD Issue 22711: -ftime-trace: semantic_analysis block is too coarse for useful profiling
- DMD Issue 22754: I really want a way to omit __monitor from Object
- DMD Issue 22769: ICE on IFTI call inside interpolated string
- DMD Issue 22925: [REG 2.113] ICE: AssertError@expression.d(508) with invalid case range statement
- DMD Issue 22980: "named arguments not allowed here" in const constructor
- DMD Issue 23065: [REG2.112.0] AA-equality fails compilation with self-referential types
- DMD Issue 23081: druntime parallel GC livelock: scanBackground spins when evStackFilled left set with empty scan stack
- DMD Issue 23133: [Reg 2.109.1] GC.collect() no longer scans in concurrent threads
- DMD Issue 23182: [REG 2.112.0] Duplicate __aaget declaration for simple expression with associative array
- DMD Issue 23320: Constness and immutability of AA keys is handled unflexibly
- DMD Issue 23359: Tuple parameter UDAs not propagated to expanded elements
Phobos bug fixes
- Phobos Issue 9783: std.variant doesn't do postblit/dtor correctly for large structs
- Phobos Issue 9871: cartesianProduct should have length for finite ranges
- Phobos Issue 10062: std.variant does not observe value semantics for large value types.
- Phobos Issue 10429: Variant and tuples by index
- Phobos Issue 10743: Nullable should implement opCmp
- Phobos Issue 10858: Documentation of std.logger: trace() does not work by default
- Phobos Issue 10916: std.format reads past end of input
- Phobos Issue 10940: Inconsistent treatment of s=0,∞ by gammaIncomplete(s, x)
- Phobos Issue 11035: std.regex: multi-digit backreference silently drops the overshooting digit
dlang.org bug fixes
- Dlang.org Issue 4380: Documentation fullyQualifiedName duplicate
- Dlang.org Issue 4424: [dmd cli.d] Some words need escaping
Contributors to this release (236)
A huge thanks goes to all the awesome people who made this release possible.
- Abhishek Bhosale
- Abul
- Abul Hossain Khan
- Adam D. Ruppe
- Adam Wilson
- Aditya Singh
- aG0aep6G
- AhmedMaged
- Airbus5717
- Albert24GG
- Aleksandr Treyger
- Alexibu
- Amaury
- Andra Maslaev
- Andrei Alexandrescu
- Andrej Mitrovic
- Andrej Petrović
- Andrey Zherikov
- apz28
- Arun Chandrasekaran
- Aryan Dadwal
- ashnaaseth2325-oss
- Ate Eskola
- Atila Neves
- Ayan Das
- Basile Burg
- Basile-z
- Bastiaan Veelo
- BBasile
- Ben Jones
- Benjamin L. Merritt
- blackbird
- blobbo
- Boris Carvajal
- Brian Schott
- brianush1
- bscuron
- BVRazvan
- carblue
- Cauterite
- Chibisi Chima-Okereke
- CHIKI
- Claude Code
- Clouuday
- Clouudy
- Codex CLI
- Craig Barnes
- Daniel Murphy
- Daniel Pflager
- Daniel Zuncke
- Dante Broggi
- David Gileadi
- David Nadlinger
- Dawson Frakes
- dd
- Dejan Lekic
- Dejan Lekić
- Demetrius Kanios
- Denis Feklushkin
- Dennis
- Dennis Korpel
- devmynote
- Dibyendu Majumdar
- dkorpel
- Dmitry Olshansky
- dokutoku
- Drehuta Andreea
- drug007
- Dumitrache Adrian-George
- e-y-e
- earthfront
- Eduard Staniloiu
- Emanuele Torre
- Emmankoko
- Emmanuel Ferdman
- Emmanuel Nyarko
- Ernesto Castellotti
- Etienne Brateau
- etienne02
- Eugene 'Vindex' Stulin
- Final Evilution
- FinalEvilution
- Florian
- Flying-Toast
- fourst4r
- Gabriel
- Gaofei Qiu
- Garrett D'Amore
- Gautam Kotian
- Giannis Vrentzos
- Giles Bathgate
- Gnav
- Grim Maple
- guai
- H. S. Teoh
- Hara Kenji
- hariprakazz
- Hiroki Noda
- Iain Buclaw
- IchorDev
- ichordev
- IchorDev
- IDONTUSEGH
- iFreilicht
- Ilya Yaroshenko
- Imperatorn
- Inkrementator
- ioanavivi12
- Iskaban10
- Jack Stouffer
- Jacob Carlborg
- Jakob Øvrum
- James S Blachly
- jamesragray
- Jan Jurzitza
- Jeremy
- Jeremy Baxter
- Jeremy DeHaan
- jibal
- jmh530
- Joakim Noah
- Joe
- Johan Engelen
- Johannes Loher
- John Kilpatrick
- Jonathan M Davis
- Jonathan Marler
- jordan4ibanez
- João Lourenço
- Julian Fondren
- K
- Kai Nacke
- Kazuya Takahashi
- Kotet
- Kuzko Sergii
- Kyle Foley
- Lander Brandt
- Laurent Tréguier
- leitimmel
- lempiji
- limepoutine
- linkrope
- Luca-Sanders
- Luís Ferreira
- Madhur Kumar
- Mai-Lapyst
- Manav Gupta
- Manu Evans
- Marc Schütz
- Marcelo Silva Nascimento Mancini
- Mark
- Mark Isaacson
- Markus Laker
- Martin Kinkelin
- Martin Nowak
- Mateiuss
- Mathias Baumann
- Mathias Lang
- Mathis Beer
- matthriscu
- Max Haughton
- MetaLang
- mhh
- Mike Franklin
- Mike Parker
- Mindy Batek
- Mingming SUN
- Mohamed El Shorbagy
- MoonlightSentinel
- Nathan Sashihara
- naydef
- Nelson Brochado
- Nicholas Wilson
- Nick Treleaven
- Nils Lankila
- nordlow
- Passw
- Patrick Schlüter
- Paul Backus
- Per Nordlöw
- Petar Kirov
- phebert5009
- Pierre Grimaud
- Pranjal Kole
- ProgramGamer
- Prthmsh7
- qchikara
- quassy
- Quirin F. Schroll
- Quirin Schroll
- Rainer Orth
- Rainer Schuetze
- Rareș Constantin
- Razvan Mihai Popa
- Razvan Nitu
- Richard (Rikki) Andrew Cattermole
- Richard Manthorpe
- Robert burner Schadek
- Robert Grancsa
- Robert Stoica
- Ross Harrison
- RubyTheRoobster
- RUSshy
- sarneaud
- Sean Enck
- Sebastian Wilzbach
- Sergii K
- Sergii Kuzko
- Shachar Shemesh
- Shriramana Sharma
- Simen Kjærås
- Sinisa Susnjar
- skoppe
- Stanislav Blinov
- Steven Dwy
- Steven Schveighoffer
- Sönke Ludwig
- Tim Schendekehl
- Timon Gehr
- Timothee Cour
- Titouan Vervack
- TJesionowski
- Tony Edgin
- tsbockman
- Tushar
- Ulrich Küttler
- Valentino Giudice
- vindexbit
- Vladimir Panteleev
- Walter Bright
- Witold Baryluk
- wolframw
- Wyatt Kennedy
- xiren7
- yelninei
- Zach Tollen