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

previous version: Previous version

Download D nightlies
To be released


Compiler changes

  1. opDispatch resolution in with statements

    opDispatch resolution in with statements has been modified to bring it into compliance with the existing scope resolution rules in the language specification. See WithStatement in the language specification.

    Prior to this release, the following code would not compile because the compiler would not be able to resolve the second call to f(), as Bar does not implement f() or opDispatch.

    import std.stdio;
    
    struct Foo
    {
        void opDispatch(string name)()
        {
            mixin("writeln(\"Foo.opDispatch!" ~ name ~ "\");");
        }
    }
    
    struct Bar
    {
        // `Bar` does not implement `f()` or `opDispatch`
    }
    
    void main()
    {
        Foo foo;
        Bar bar;
    
        with(foo)
        {
            f();       // prints "Foo.opDispatch!f"
            with(bar)
            {
                f();   // Prior to this Release: Error: undefined identifer `f`
                       // Starting with  this release: Prints "Foo.opDispatch!f".
                       // `f`'s resolution is forwarded up the scope hierarchy.
            }
        }
    }
    

    Starting with this release, the second call to f() will be forwarded up the scope hierarchy, matching the implementation of Foo.opDispatch.

  2. Generate header files using do instead of body as per DIP1003

    Support for DIP1003 was added in release 2.075.0. Use of body in an error message and header file generation has been fixed in this release.

  3. pragma(crt_constructor) and pragma(crt_destructor) were added

    This allows programs to run initialization code before or cleanup code after C main.

    In particular those pragmas can be used in -betterC code as substitutes for shared static this() and shared static ~this().

    Note: At the time of execution druntime is not initialized.

    Note: The order in which constructors are executed is unspecified.

    import core.stdc.stdio;
    
    pragma(crt_constructor)
    void init()
    {
        puts("init");
    }
    
    pragma(crt_destructor)
    void fini()
    {
        puts("fini");
    }
    
    extern(C) int main()
    {
        puts("main");
    }
    
  4. Subtraction of pointers that point to different types has been deprecated.

    The language specification reads: "If both operands are pointers, and the operator is -, the pointers are subtracted and the result is divided by the size of the type pointed to by the operands. It is an error if the pointers point to different types."

    Prior to this release, the compiler allowed the following pointer arithmetic:

    void* p1;
    int* p2;
    auto p3 = p2 - p1;
    

    Starting with this release, the subtraction of pointers that point to different types will emit a deprecation warning. The warning will remain in place for at least a year, after which it will be changed to an error.

  5. Variadic template arguments no longer require alias workaround

    Prior to this release, the following code would not compile.

    class C(Types...)
    {
        void apply(U)(U delegate(Types[0]) f0) { }  // Error: cannot deduce function from argument types
    }
    
    void test()
    {
        C!int c;
    
        int f(int) { return 0; }
    
        c.apply(&f);
    }
    

    It could be worked around by aliasing the individual template arguments.

    class C(Types...)
    {
        alias Types[0] T0;
        void apply(U)(U delegate(T0) f0) { }  // OK
    }
    

    Starting with this release, the workaround is no longer required.

  6. fix Issue 16997 - Integral promotion rules not being followed for unary + - ~ operators

    To follow the C integral promotion rules, types are promoted to int before the unary + - or ~ operators are applied. Existing D does not do this.

    This is corrected when one of the following command line switches are used:

    -transition=intpromote -transition=16997

    It affects operands of type byte, ubyte, short, ushort, char, and wchar. The operands are promoted to int before the operator is applied. The result type will now be int.

    The values computed will be different for some values of the operands:

    * All values of - and ~ applied to ubyte, ushort, char, and dchar operands will change.

    * The values of -byte(-128) and -short(-32768) will change.

    If one of the '-transition=' switches is not specified, a deprecation will be emitted for these operations. To fix the deprecation and work as desired with or without the '-transition=' switch:

    Option 1:

    Rewrite op b as op int(b) to use correct, i.e. C-like, behavior.

    Option 2:

    Rewrite op b as typeof(b)(op int(b)) to preserve the old behavior.

    Once deprecated this will become an error, and then the C-like behavior will become the default.

  7. Optional ModuleInfo

    ModuleInfo is not a necessary feature of D. There are use cases where the program author may choose to provide an alternate implementation of the D runtime that does not include ModuleInfo. This could be for interoperability with other software or to reduce the footprint of D programs in resource constrained platforms.

    Prior to this release, the compiler would emit an error if ModuleInfo was not declared in the D runtime.

    Platform support is provided by the D runtime. ModuleInfo is declared in object.d. Therefore, the compiler can see, at compile-time, whether or not the platform has provided support for ModuleInfo and generate object code accordingly.

    Starting with this release, if ModuleInfo is not declared in the D runtime, the compiler will simply not generate ModuleInfo instances.

    This should reduce friction for those wishing to incrementally port D to new platforms and use D in a more pay-as-you-go fashion.

  8. Added RAII and try-finally support for -betterC mode.

    This also means that scope(exit) statements will also work, because they are internally lowered to try-finally statements.

    This does not mean that exceptions are supported. Throwing, catching, and stack frame unwinding is not supported, as that requires support from Phobos.

    It means that, for RAII, when variables go out of lexical scope their destructors get run. When try blocks from try-finally statements exit, the code in the finally block is run.

  9. Windows: dmd can now detect Visual Studio installation paths

    dmd now determines the environment variables that are usually set if the "Command Line prompt for Visual Studio" link is used to open a console window (or vcvarsall.bat is executed). This includes Windows SDK directories and Visual C library directories. When compiling with -m64 or -m32mscoff this allows dmd to invoke the linker without having a Visual Studio version preselected by the installer.

    In particular, the variables WindowsSdkDir, WindowsSdkVersion, UniversalCRTSdkDir, UCRTVersion, VSINSTALLDIR, VisualStudioVersion, VCINSTALLDIR and VCTOOLSINSTALLDIR are determined from the registry if they are not set in the environment or sc.ini.

    Supported Windows SDK range from 7.0A to 10.x, supported Visual Studio Versions are VS 2008 to VS 2017. If multiple versions are installed, highest versions are preferred.

Runtime changes

  1. core.runtime now allows more fine-grained control over unittests.

    core.runtime.extendedModuleUnitTester property allows specifying information about the tests run, and how to handle the result. See documentation for core.runtime.UnitTestResult for details.

    core.runtime.moduleUnitTester (setting a unittest handler that returns bool) will continue to be supported for legacy projects.

    import core.runtime;
    import core.stdc.stdio: printf;
    
    UnitTestResult customTester()
    {
        UnitTestResult ret;
    
        // run only the tests in my package
        immutable prefix = "myPackage.";
        foreach (m; ModuleInfo)
        {
            if (m.unitTest !is null && m.name.length >= prefix.length &&
                  m.name[0 .. prefix.length] == prefix)
            {
                ++ret.executed; // count unit tests run
                try
                {
                    m.unitTest();
                    ++ret.passed; // count unit tests passed
                }
                catch(Throwable t)
                {
                    auto msg = t.toString();
                    printf("%.*s\n", cast(uint)msg.length, msg.ptr);
                }
            }
        }
        // always summarize
        ret.summarize = true;
        // only unit testing, don't ever run main
        ret.runMain = false;
    }
    
    version(unittest) static shared this()
    {
        Runtime.extendedModuleUnitTester = &customTester;
    }
    
  2. The runtime learned a few new options to customize how coverage reports are created.

    The format is the same as with the GC but using covopt as suffix, for example you can pass options separating them with spaces like `--DRT-covopt "merge:1 dstpath:/tmp"`.

    These are the currently accepted options:

    merge
    Merge the current run with existing reports if 1, or overwrite the existing reports if 0.
    srcpath
    Set path to where source files are located.
    dstpath
    Set path to where listing files are to be written.

Library changes

  1. mean Was Added To std.algorithm

    std.algorithm.iteration.mean accurately finds the mean (a.k.a the average) of any range of number-like elements.

    import std.algorithm.iteration : mean;
    import std.math : approxEqual;
    
    int[] arr1 = [1, 2, 3];
    real[] arr2 = [1.5, 2.5, 12.5];
    
    assert(arr1.mean.approxEqual(2));
    assert(arr2.mean.approxEqual(5.5));
    
    // user defined number types also work
    import std.bigint : BigInt;
    
    auto bigint_arr = [
        BigInt("1_000_000_000_000_000_000"),
        BigInt("2_000_000_000_000_000_000"),
        BigInt("3_000_000_000_000_000_000"),
        BigInt("6_000_000_000_000_000_000")
    ];
    auto seed = BigInt(0);
    assert(bigint_arr.mean(seed) == BigInt("3_000_000_000_000_000_000"));
    
  2. findSkip can now skip elements using just a predicate function

    Previously, std.algorithm.searching.findSkip could only be used to find a specific string. Now, a new overload allows elements in a range to be skipped over if the passed function returns true:

    import std.ascii : isWhite;
    string s = "   abc";
    assert(findSkip!isWhite(s) == 3 && s == "abc");
    assert(!findSkip!isWhite(s) && s == "abc");
    
  3. std.array.byPair now returns a NamedTuple

    std.array.byPair now returns a named tuple.

    import std.array : byPair;
    import std.typecons : Tuple;
    
    int[string] dict = ["b": 2, "c": 3];
    auto pairs = dict.byPair;
    static assert(is(typeof(pairs.front) : Tuple!(string,int)));
    
    // access by index (existing way)
    assert(pairs.front[0] == "b");
    assert(pairs.front[1] == 2);
    
    // access by name (enabled with this release)
    assert(pairs.front.key == "b");
    assert(pairs.front.value == 2);
    
  4. std.container.dlist supports linearRemoveElement

    linearRemoveElement removes the first occurence of an element from the dlist

    import std.container : Dlist;
    import std.algorithm.comparison : equal;
    
    auto a = DList!int(-1, 1, 2, 1, 3, 4);
    a.linearRemoveElement(1);
    assert(equal(a[], [-1, 2, 1, 3, 4]));
    
  5. std.container.slist supports linearRemoveElement

    linearRemoveElement removes the first occurence of an element from the slist

    import std.container : Slist;
    import std.algorithm.comparison : equal;
    
    auto a = SList!int(-1, 1, 2, 1, 3, 4);
    a.linearRemoveElement(1);
    assert(equal(a[], [-1, 2, 1, 3, 4]));
    
  6. Deprecate save for std.range.package.Transposed

    Transposed is incorrectly marked as a forward range. Its popFront primitive cannot be used without affecting any other copies made with save. save will be removed from Transposed in November 2018.

    auto x = [[1,2,3],[4,5,6]].transposed;
    auto y = x.save;
    y.popFront;
    assert(x.equal([[1,4],[2,5],[3,6]])); // FAILS, x is really [[2,5],[3,6]]
    

    For more details, please see the respective Bugzilla issue.

  7. std.typecons.Ternary.opBinary supports bool bitwise operands

    Now std.typecons.Ternary can be used in bitwise operations with bools:

    import std.typecons : Ternary;
    
    Ternary a = Ternary(true);
    assert(a == Ternary.yes);
    assert((a & false) == Ternary.no);
    assert((a | false) == Ternary.yes);
    assert((a ^ true) == Ternary.no);
    assert((a ^ false) == Ternary.yes);
    
previous version: Previous version