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

previous version: 2.108.1

Download D 2.109.0 Beta
to be released Jun 01, 2024

2.109.0 comes with 14 major changes and 22 fixed Bugzilla issues. A huge thanks goes to the 41 contributors who made 2.109.0 possible.

List of all bug fixes and enhancements in D 2.109.0.

Compiler changes

  1. [next edition] Aliasing a member of a type instance is now an error

    Such an alias actually aliases a member of the instance's type, not the instance member itself. That could be confusing, and is now an error. Instead, alias a member of the type:

    struct Foo
        int v;
        void test(Foo that) const
            alias a = this.v; // OK
            alias b = that.v; // Error, use `typeof(that).v` instead
            assert(&a is &b); // passes
            assert(&b !is &that.v);
    struct Bar
        Foo f;
        alias v = f.v; // Error, use `typeof(f).v`
  2. Add Bitfield Introspection Capability


    • property .bitoffsetof to get the starting bit number of a bitfield

    • property .bitwidth to get the number of bits in a bitfield

    • __traits(isBitfield, symbol) returns true if a field symbol is a bitfield

  3. Added __ctfeWrite to write messages from CTFE

    The special function __ctfeWrite can now be used to write messages during CTFE, similar to pragma(msg, ...). It is Implementation Defined how the message is presented to the user; the recommended way is by printing the message to stderr, standard error stream. The function is available in object.d and accepts any value implicitly convertible to const(char)[].

    For example:

    int greeting()
        __ctfeWrite("Hello from CTFE. Today is ");
        return 0;
    enum forceCTFE = greeting();

    Compiling this program will generate the following output:

    Hello from CTFE. Today is <current date>
  4. Deprecation warnings are now also limited by -verrors

    By default, the compiler stops after 20 error messages, unless a different amount is specified by passing e.g. -verrors=50 or -verrors=0 for no limit. This error limit now also applies to deprecation messages, so the command line isn't flooded with hundreds of them when compiling a big project that hasn't fixed all deprecations yet.

    deprecated void f()
    void main()

    > dmd -verrors=3 app.d
    app.d(7): Deprecation: function deprecationlimit.x is deprecated
    app.d(8): Deprecation: function deprecationlimit.x is deprecated
    app.d(9): Deprecation: function deprecationlimit.x is deprecated
    1 deprecation warning omitted, use -verrors=0 to show all

  5. dtoh generates signatures for extern(Windows) and extern(System) functions.

    When using the -HC switch, in addition to extern(C) and extern(C++) functions, extern(Windows) and extern(System) functions are output in the .h file as well.

    Example D module:

    extern(Windows) int hello()
        return 0;
    extern(System) int myFriend()
        return 0;

    Output with -HC switch:

    // (full header omitted)
    #ifndef _WIN32
    #define EXTERN_SYSTEM_AFTER __stdcall
    #define EXTERN_SYSTEM_BEFORE extern "C"
    int32_t __stdcall hello();
  6. foreach_reverse on a delegate is now an error

    The compiler did not try to implement reverse traversal of the results returned by the delegate when foreach_reverse was used. That could result in code that was confusing to read, so it was deprecated (for many years). Using foreach_reverse with a delegate is now an error.

  7. Expansion of identifier tables to allow new characters to match C23 have been added along with CLI configurability

    You can currently choose between c99, c11, UAX31 (C23's) and all (the least restrictive set) for both D and ImportC.

    This can be done with -identifiers=<table> and for ImportC -identifiers-importc=<table>.

    The default table for D is currently set to all, while ImportC is set to c11. Previously both D and ImportC used the c99 tables.

    D's table will be swapped over at a later date to UAX31, this should be done in 2.117. If you find yourself at this time using c99 specific characters and not willing to change them, you may switch back to all. Although it should be unlikely that you will need to.

  8. ImportC has improved Unicode support

    Universal Character Names are now supported, allowing you to use the \uXXXX and \UXXXXXXXX syntax where X is a hex digit as part of an identifier.

    DigitalMars sppn does not support anything newer than C99. It is known to be limited and using any Unicode character not in those ranges will result in an error.

  9. Missing symbol errors are made less cryptic

    It is not uncommon to forget to link a library, list a .d file on the command line, or include a main function. Example:

    module app;
        import assertions;
        assertEquals('D', 'D');
    module assertions;
    void assertEquals(char a, char b)
        assert(a == b);

    When compiling this as follows:

    > dmd -unittest app.d

    The compiler would not see any error, but at link time there are missing symbols. Formerly, this would result in a cryptic linker error with mangled symbol names:

    /usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../lib/Scrt1.o: in function `_start':
    (.text+0x1b): undefined reference to `main'
    /usr/bin/ld: app.o: in function `_D3app16__unittest_L5_C1FZv':
    app.d:(.text._D3app16__unittest_L5_C1FZv[_D3app16__unittest_L5_C1FZv]+0xc): undefined reference to `_D10assertions12assertEqualsFaaZv'
    collect2: error: ld returned 1 exit status

    Experienced users might know how to demangle the symbol to make it more readable:

    > echo _D10assertions12assertEqualsFaaZv | ddemangle
    void assertions.assertEquals(char, char)

    But this is inconvenient to do manually every time. Now, when the compiler invokes the linker program, it will read its output, scan for undefined symbol errors, and demangle the names:

    Error: undefined reference to main
    Error: undefined reference to void assertions.assertEquals(char, char)
           referenced from void app.__unittest_L5_C1()
           perhaps define a void main() {} function or use the -main switch
           perhaps .d files need to be added on the command line, or use -i to compile imports
    Error: linker exited with status 1

    Which makes it easier to fix the command:

    > dmd -unittest -main -i app.d

    Currently supported linkers are ld, bfd, gold, mold, and Microsoft LINK. Please file an issue if your linker's errors aren't detected.

  10. Windows OMF support has been removed

    After two years of making PE-COFF support the default on Windows, OMF support has now been removed.

    This includes the switch (-m32omf).

Runtime changes

  1. Add module core.sys.linux.sys.mount.

    The new module core.sys.linux.sys.mount provides definitions corresponding to those in the header <sys/mount.h> on Linux.

  2. Remove all collectNoStack functions and API from druntime.

    The function collectNoStack in the D garbage collector performed a collection, without using any roots from thread stacks or thread-local-storage. The danger of running this mechanism is that any blocks of memory which only have a reference from a thread might be collected, while the thread is still running and possibly using the memory.

    The only time this function was called was at GC termination. At GC termination, the GC is about to be destroyed, and so we want to run as many destructors as possible. However, if some thread is using GC-allocated memory, cleaning up that memory isn't going to help matters. Either it will crash after the GC cleans the memory, or it will crash after the GC is destroyed.

    The original purpose of this function (from D1) was to ensure simple uses of the GC were cleaned up in small test programs, as this mechanism was only used on single-threaded programs (and of course, at program exit). Also note at the time, D1 was 32-bit, and false pointers where much more common. Avoiding scanning stacks would aid in avoiding seemingly random behavior in cleanup. However, as shown below, there are more deterministic ways to ensure data is always cleaned up.

    Today, the dangers are much greater that such a function is even callable -- any call to such a function would immediately start use-after-free memory corruption in any thread that is still running. Therefore, we are removing the functionality entirely, and simply doing a standard GC cleanup (scanning stacks and all). One less footgun is the benefit for having less guaranteed GC clean up at program exit.

    In addition, the GC today is a bit smarter about where the valid stack is, so there is even less of a chance of leaving blocks unfinalized.

    As always, the GC is not guaranteed to clean up any block at the end of runtime. Any change in behavior with code that had blocks clean up before, but no longer are cleaned up is still within specification. And if you want the behavior that absolutely cleans all blocks, you can use the --DRT-gcopt=cleanup:finalize druntime configuration option, which will clean up all blocks without even scanning.

  3. Mark Thread.sleep as @trusted

    The static method core.thread.Thread.sleep is now marked as @trusted and can be called directly from @safe code.

Library changes

  1. Add std.process.Config.preExecDelegate

    std.process.Config.preExecDelegate is just like std.process.Config.preExecFunction, but can capture an environment, for example:

    import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl;
    import std.process : Config, execute;
    void runProgram(int pdeathsig)
            config: Config(
                preExecDelegate: () @trusted =>
                    prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1,

    preExecFunction is retained for backwards compatibility. If both preExecFunction and preExecDelegate are given, both are called.

List of all bug fixes and enhancements in D 2.109.0:

DMD Compiler bug fixes

  1. Bugzilla 14128: AliasDeclaration allows expressions, causing false code for ThisExp
  2. Bugzilla 20148: void initializated bool can be both true and false
  3. Bugzilla 21854: @live breaks foreach over integers
  4. Bugzilla 21923: @live does not take destructor code into account.
  5. Bugzilla 22977: [dip1000] can escape scope pointer returned by nested function
  6. Bugzilla 23530: casting immutable away allowed in safe
  7. Bugzilla 24434: Casting away const with cast() should not produce an lvalue
  8. Bugzilla 24477: Union access of bool shouldn't be allowed in @safe
  9. Bugzilla 24485: Invalid implicit ref return reinterpret cast for structs with copy constructor
  10. Bugzilla 24493: FreeBSD_14 version identifier missing
  11. Bugzilla 24520: [REG] type(value) got a synonym (type)(value)
  12. Bugzilla 24525: auto ref lambda exp not parsed if used as left-most expression in an expression statement

DMD Compiler enhancements

  1. Bugzilla 5573: Compiler (not linker) should generate an error for missing main()
  2. Bugzilla 21718: Preview switches have insufficient descriptions
  3. Bugzilla 24111: [ImportC] fatal error C1034: stdio.h: no include path set
  4. Bugzilla 24450: apply VRP to foreach indices when array is of known length
  5. Bugzilla 24452: Can't disable coverage at runtime

Phobos bug fixes

  1. Bugzilla 15708: std.range.choose assumes hasElaborateCopyConstructor means "has __postblit"
  2. Bugzilla 24478: std.csv array out of bounds when row size exceeds header

Druntime bug fixes

  1. Bugzilla 24517: druntime tests fail on FreeBSD 14 bug fixes

  1. Bugzilla 24472: __traits(fullyQualifedName) is undocumented in spec enhancements

  1. Bugzilla 24488: contributor guide hard to find from home page

Contributors to this release (41)

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

previous version: 2.108.1