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.113.0

previous version: 2.112.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


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

List of all upcoming bug fixes and enhancements in D 2.113.0.

Compiler changes

  1. 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
    }
    
  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.

  3. 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.

  4. 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.

  5. 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).

  6. 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]));
    
  7. 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.

  8. 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.

  9. 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
    }
    
  10. 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
    
  11. 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.

  12. -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
    

  13. 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

  1. 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

  1. 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

  1. 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:
previous version: 2.112.0