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: 2.078.3 – next version: 2.079.1

Download D 2.079.0
released Mar 01, 2018


List of all bug fixes and enhancements in D 2.079.0.

Compiler changes

  1. Argument mismatch errors have been improved

    dmd now shows which argument failed to match a parameter with an explanatory error message.

    void fill(ref char[16] buf, char c);
    
    void main()
    {
        fill("1234567890123456", '*');
    
        const char[16] s;
        fill(s, '*');
    }
    

    Output:

    fillchar.d(5): Error: function fillchar.fill(ref char[16] buf, char c) is not callable using argument types (string, char)
    fillchar.d(5):        cannot pass rvalue argument "1234567890123456" of type string to parameter ref char[16] buf
    fillchar.d(8): Error: function fillchar.fill(ref char[16] buf, char c) is not callable using argument types (const(char[16]), char)
    fillchar.d(8):        cannot pass argument s of type const(char[16]) to parameter ref char[16] buf
    

    Note: Currently this change doesn't apply when the function has overloads.

  2. The deprecation period of using the result of comma expression has ended

    Comma expressions have proven to be a frequent source of confusion, and bugs. Using their result will now trigger an error message.

    The comma operator (,) allows executing multiple expressions and discards the result of them except for the last which is returned.

    int a = 1;
    int b = 2;
    bool ret = a == 2, b == 2; // true
    

    It's also common to use the comma operator in for-loop increment statements to allow multiple expressions.

    for (; !a.empty && !b.empty; a.popFront, b.popFront)
    

    Hence, using the comma operator in for-loop increment statements is still allowed.

    Corrective Action

    If possible, split the comma operator in two statements. Otherwise use lambdas.

    auto result = foo(), bar();
    
    // split off in two statements
    foo();
    auto result = bar();
    
    // or use lambdas
    auto result = {foo(); return bar();}();
    

    Rationale

    The comma operator leads to unintended behavior (see below for a selection) Moreover it is not commonly used and it blocks the ability to implement tuples as a language feature using commas.

    A selection of problems through the accidental use of the comma operator:

    writeln( 6, mixin("7,8"), 9 ); // 6, 8, 9
    
    struct Vec
    {
        this(T...)(T args) { ... }
    }
    // missing type name
    Vec v = (0, 0, 3); // Vec(3)
    
    int a = 0;
    int b = 2;
    if (a == 1, b == 2) {
        // will always be reached
    }
    
    void foo(int x, int y=0) {}
    foo((a, b)); // Oops, foo(b) is called
    
    synchronized (lockA, lockB) {}
    // multiple expressions aren't currently implemented, but it compiles due to the comma operator
    

  3. Function parameters with default values are now allowed after variadic template parameters

    Function parameters with default values are now allowed after variadic template parameters and when IFTI is used, always take their default values. This allows using special tokens (eg __FILE__) after variadic parameters, which was previously impossible.

    For example:

    string log(T...)(T a, string file = __FILE__, int line = __LINE__)
    {
      return text(file, ":", line, " ", a);
    }
    
    assert(log(10, "abc") == text(__FILE__, ":", __LINE__, " 10abc"));
    

    This should be preferred to the previous workaround, which causes a new template instantiation for every invocation:

    string log(string file = __FILE__, int line = __LINE__, T...)(T a);
    
  4. The delete keyword has been deprecated.

    See the Deprecated Features for more information.

    Starting with this release, using the delete keyword will result in a deprecation warning.

    As a replacement, users are encouraged to use destroy if feasible, or core.memory.__delete as a last resort.

  5. The deprecation period of the -dylib flag on OSX has ended. Use -shared

    The deprecation period of the -dylib flag on OSX has ended.

    dmd -dylib awesome_d_library.d

    Use the -shared flag to generate a shared library:

    dmd -shared awesome_d_library.d

  6. Experimental @nogc Exception throwing with -dip1008

    DIP 1008 has been merged and it can be previewed under the experimental -dip1008 flag:

    void main() @nogc
    {
        throw new Exception("I'm @nogc now");
    }
    

    rdmd -dip1008 app.d

  7. A compiler trait used to detect if a function is marked with @disable has been added.

    Prior to this release is was impossible to filter out @disable functions without using __trait(compiles), which was less than ideal since false could be returned for other reasons.

    Now, in metaprogramming code, @disable functions can be detected accurately, using __traits(isDisabled) and even in overload sets:

    module runnable;
    
    struct Foo
    {
        import std.stdio;
        @disable static void foo() {__PRETTY_FUNCTION__.writeln;}
        static void foo(int v) {__PRETTY_FUNCTION__.writeln;}
        static void bar() {__PRETTY_FUNCTION__.writeln;}
        @disable static void bar(int v) {__PRETTY_FUNCTION__.writeln;}
    }
    
    void test(T)()
    {
        foreach (member; __traits(allMembers, T))
            foreach (overload; __traits(getOverloads, T, member))
                static if (!__traits(isDisabled, overload))
        {
            static if (is(typeof(&overload) == void function()))
                overload();
            else static if (is(typeof(&overload) == void function(int)))
                overload(42);
        }
    }
    
    void main(){test!Foo;}
    

    prints:

    void runnable.Foo.foo(int v)
    void runnable.Foo.bar()
    

  8. Fix Issue 17630 - selective imports find symbols in private imports of other modules

    Selectively importing a symbol should work only if the symbol imported is defined or publicly imported in the imported module. Due to a compiler bug, selectively importing a symbol works even if the symbol is defined in a privately imported module in the imported module.

    //a.d
    int bar;
    
    //b.d
    import a;
    
    //c.d
    import b : bar;
    

    The above code will now result in a deprecation message which states that bar cannot be accessed since it is privately imported in b.

  9. Fix issue 17899 - Allow delegates to be initialised at compile time

    delegates may now be initialised at module scope. This changes the effect of the fix for 13259 (turning the ICE that resulted into an error) making the follow legal:

    void delegate() bar = (){};
    

    The function pointer is set to the function of the delegate, the context pointer is set to null.

  10. Fix Issue 18053 - mangle long/ulong as int64_t/uint64_t

    This is a breaking change (on OSX 64).

    Due to the erratic implementation defined behavior of C++ name mangling, it was difficult to get D's long/ulong to portably match up with the corresponding C++ compiler.

    By instead relying on how the corresponding C++ compiler mangled int64_t/uint64_t it makes the C++ side of the D<=>C++ interface much simpler.

    For the current platforms dmd supports, only the OSX 64 bit mangling changes. In this case from 'm' to 'y'.

    Note: int64_t and uint64_t are defined in stdint.h

  11. Fix Issue 18219 - Private import inside struct leaks symbols when used as VarDeclaration types

    When implementing a struct which has a local private import the imported symbols are leaked if present in a VarDeclaration statement. For more information and examples see : https://issues.dlang.org/show_bug.cgi?id=18219

    Symbols from the private import should not visible outside the struct scope. A deprecation is now issued when such cases are encountered

  12. Fix issue 18361 - Ddoc ability to opt-out of automatic keyword highlighting in running text

    Currently, ddoc automatically highlights all occurrences of words in running text that coincide with the symbol or module being documented, or a parameter name of a function being documented. While convenient, it often caused unintended highlighting of normal words in text when module, function, or parameter identifiers coincide with normal words. This led to a proliferation of prefixing words with _ in order to suppress this behaviour.

    Now a better solution has been implemented to completely opt-out of this feature via the DDOC_AUTO_PSYMBOL, DDOC_AUTO_KEYWORD, and DDOC_AUTO_PARAM macros, which are used for all such automatically-highlighted words in running text. Occurrences of module, function, or parameter names inside code blocks are not included. By default, these macros simply redirect to DDOC_PSYMBOL, DDOC_KEYWORD, and DDOC_PARAM, but the user can now redefine these macros so that they simply expand to the word itself without any highlighting:

    DDOC_AUTO_PSYMBOL = $0
    DDOC_AUTO_KEYWORD = $0
    DDOC_AUTO_PARAM = $0
    

    Furthermore, whenever a word is prefixed with _ to suppress automatic highlighting, it is now wrapped in the DDOC_AUTO_PSYMBOL_SUPPRESS macro. This is to provide users who wish to opt out of automatic highlighting an easy way to find all occurrences of these underscore prefixes so that they can be removed from the text. For example, they can redefine this macro to something highly-visible and easily searched for, such as:

    DDOC_AUTO_PSYMBOL_SUPPRESS = FIXME_UNDERSCORE_PREFIX 
    

    and then search the generated documentation for the string FIXME_UNDERSCORE_PREFIX and delete the _ prefix from all corresponding parts of the documentation comment text.

  13. D ABI change on Win32 and OSX targets

    The compiler has been updated to prefix all extern(D) symbols with an extra underscore where the platform expects one on all external symbols. This allows compiled code to work better with binutil programs, such as the ability to list symbols demangled inside a debugger.

    This is an ABI breaking change and requires recompiling libraries.

  14. HexString literals are deprecated.

    HexString literals are deprecated. Use std.conv.hexString instead.

  15. Added the -i command line option to automatically include imports

    Added the command line option -i which causes the compiler to treat imported modules as if they were given on the command line. The option also accepts "module patterns" that include/exclude modules based on their name. For example, the following will include all modules whose names start with foo, except for those that start with foo.bar:

    dmd -i=foo -i=-foo.bar

    The option -i by itself is equivalent to:

    dmd -i=-std -i=-core -i=-etc -i=-object

  16. Added -Xi=<name> to include more fields in the JSON output

    Added -Xi=<name> to include more fields in the JSON output. Currently there are 4 fields that can be included: "compilerInfo", "buildInfo", "modules" and "semantics", i.e.

    dmd -Xi=compilerInfo -Xi=buildInfo -Xi=semantics -Xi=modules

    will generate a JSON file with the following:

    {
      "compilerInfo" : {
        "binary" : "<filename-of-compiler-binary>",
        "version" : "<compiler-version>",
        "supportsIncludeImports" : true,
      },
      "buildInfo" : {
        "config" : "<config-filename>",
        "cwd" : "<cwd-during-build>",
        "importParths" : [
          "<import-path1>",
          "<import-path2>",
          // ...
        ]
      },
      "semantics" : {
        "modules" : [
          {
            "name" : "<module-name>",
            "file" : "<module-filename>",
            "isRoot" : true|false
          },
          // more module objects...
        ]
      },
      "modules" : [
        // an array of the syntax data for all the modules,
        // this is the same array that would be generated
        // for a JSON file with no -Xi=<field> options
      ]
    }
    

    If JSON is generated without any -Xi= options then the old format is used. The old format is the same data that would appear in the new "modules" field.

    Also note that the compiler can now be invoked with no source files as long as at least one JSON field is provided, i.e.

    dmd -Xi=compilerInfo

    This is an experimental command-line flag and will be stabilized in the next release.

  17. Lambda comparison using __traits(isSame, ...)

    It is now possible to compare two lambda functions, under certain constraints, using __traits(isSame, lamda1, lambda2). In order to correctly compare two lambdas, the following conditions must be satisfied:

    • the lambda function arguments must not have a template instantiation as an explicit argument type. Any other argument types (basic, user-defined, template) are supported.
    • the lambda function body must contain a single expression (no return statement) which contains only numeric values, manifest constants, enum values and arguments. If the expression contains local variables, function calls or return statements, the function is considered uncomparable.

    These limitations might be lifted in the next release version.

    Whenever a lambda is considered uncomparable, the __traits(isSame, ...) expression in which it's used will return false, no matter what other lambda is used in the comparison.

  18. Windows: Visual C++ and the Windows SDK are no longer required to build 64-bit executables

    The Windows installer now adds platform libraries built from the MinGW definitions and a wrapper library for the VC2010 shared C runtime. When building COFF object files with -m64 or -m32mscoff and no Visual Studio installation is detected or no platform libraries are found these will be used as replacements. If the Microsoft linker is not found, the LLVM linker LLD will be used.

  19. Using D with no/minimal/custom runtime implementation in a pay-as-you-go fashion

    DMD has been further decoupled from the runtime so it is now easier and more convenient to use D without the runtime in a pay-as-you-go fashion. This will be of interest to users wishing to incrementally or partially port D to new platforms, users targeting bare-metal or resource constrained platforms, and users wishing to use D as a library from other languages without the runtime.

    Prior to this release, if one attempted to compile a simple D application that made no use of any runtime features, the compiler would have emitted a number of errors about a missing object.d, a missing Error class, missing TypeInfo, and missing ModuleInfo, among others.

    Starting with this release, one can now create a library for use from another language, requiring only the .d source file that implements that library, and an empty object.d file

    Example 1

    module object
    
    module math;
    
    extern(C) int add(int a, int b)
    {
        return a + b;
    }
    

    dmd -conf= -lib math.d
    size math.a
       text    data     bss     dec     hex filename
          0       0       0       0       0 math.o (ex math.a)
         20       0       0      20      14 math_1_129.o (ex math.a)
    

    Also, starting with this release, one can now create very small executables with a minimal runtime implementation.

    Example 2

    DMD auto-generates a call to _d_run_main which, in turn, calls the user-defined main function. DMD automatically generates a call to g++ which links in the C runtime.

    module object;
    
    private alias extern(C) int function(char[][] args) MainFunc;
    private extern (C) int _d_run_main(int argc, char** argv, MainFunc mainFunc)
    {
        return mainFunc(null);  // assumes `void main()` for simplicity
    }
    
    module main;
    
    void main() { }
    

    dmd -conf= -defaultlib= -fPIC main.d object.d -of=main
    size main
       text    data     bss     dec     hex filename
       1403     584      16    2003     7d3 main
    

    Example 3

    Manually generated call to main. No C runtime.

    module object;
    
    extern(C) void __d_sys_exit(long arg1)
    {
        asm
        {
            mov RAX, 60;
            mov RDI, arg1;
            syscall;
        }
    }
    
    extern void main();
    private extern(C) void _start()
    {
        main();
        __d_sys_exit(0);
    }
    
    module main;
    
    void main() { }
    

    dmd -c -lib main.d object.d -of=main.o
    ld main.o -o main
    size main
       text    data     bss     dec     hex filename
         56       0       0      56      38 main
    

    Usage of more advanced D features (e.g. classes, exceptions, etc...) will require runtime implementation code, but they can be implemented in a pay-as-you-go fashion.

  20. macOS deployment target was increased to 10.9

    The compiler has been updated to use 10.9 and link with libc++ on OSX. This is due the shared libstdc++ library from older versions of macOS having compatibility issues with the headers included in a modern XCode.

    The minimum required version of running the compiler is now Mac OS X Mavericks (10.9).

  21. Deprecate the use of selectively imported private members

    The specification states that a private member is visible only from within the same module. Prior to this release, due to a bug, private members were visible through selective imports from other modules, violating the specification. Beginning with this release, accessing private members from outside the module in which they are declared will result in a deprecation message.

  22. .ptr on arrays can no longer be used in @safe code

    The deprecation period for using .ptr on arrays in @safe ended. The following now triggers an error instead of a deprecation:

    @safe ubyte* oops(ubyte[] arr) {
        return arr.ptr;
    }
    

    Use &arr[0] instead:

    @safe ubyte* oops(ubyte[] arr) {
        return &arr[0];
    }
    

    Note that this only applies to SafeD - in @system code .ptr may still be used:

    @system ubyte* oops(ubyte[] arr) {
        return arr.ptr;
    }
    

Runtime changes

  1. core.memory.__delete has been added

    core.memory.__delete allows easy migration from the deprecated delete. __delete behaves exactly like delete:

    bool dtorCalled;
    class B
    {
        int test;
        ~this()
        {
            dtorCalled = true;
        }
    }
    B b = new B();
    B a = b;
    b.test = 10;
    
    __delete(b);
    assert(b is null);
    assert(dtorCalled);
    // but be careful, a still points to it
    assert(a !is null);
    

    For example, on a Posix platform you can simply run:

    sed "s/delete \(.*\);/__delete(\1);/" -i **/*.d
    

    Users should prefer object.destroy` to explicitly finalize objects, and only resort to core.memory.__delete when object.destroy would not be a feasible option.

  2. The garbage collector is now lazily initialized on first use

    The runtime now lazily initializes the GC on first use, thus allowing applications that do not use the GC to skip its initialization.

Library changes

  1. std.format with strings passed during compile-time has been optimized

    Giving std.format.format a string as a template parameter allows the type correctness of the parameters to be checked at compile time:

    import std.format : format;
    
    auto s1 = format!"%d"(4); // works fine
    auto s2 = format("%d"); // runtime exception
    auto s3 = format!"%d"(); // compile time error
    

    Now, using this overload also allows std.format to make an educated guess at the length of the resulting string, reducing the total number of reallocations made to the output buffer.

    import std.format : format;
    
    auto s1 = format!"%02d:%02d:%02d"(10, 30, 50); // known for certain to be 8 chars long
    auto s2 = format!"%s %d"("Error Code: ", 42); // Makes an educated guess
    

    Longer format strings benefit the most from this change.

  2. Changed std.conv.hexString to return an immutable string literal

    std.conv.hexString now returns an immutable string literal rather than an array of ubytes. This is more in keeping with the documentation that says it is a replacement for the deprecated x"deadbeef" string literal syntax.

    The benefits of this change are:

    • behavior consistency with the documentation
    • the hexString template instantiations no longer appear in the generated object file
    • the generated object file no longer contains references to TypeInfo and various druntime functions
    • it is now compatible with -betterC mode

    In some cases, code did rely on it being an array, and a cast will fix the issue:

    // now an error:
    enum ubyte[8] input = hexString!"c3 fc 3d 7e fb ea dd aa";
    
    // add cast to fix:
    enum ubyte[8] input = cast(ubyte[8]) hexString!"c3 fc 3d 7e fb ea dd aa";
    
  3. Nullable!C.nullify no longer calls .destroy when C is a class or interface

    Previously, when .nullify is called on a Nullable!C where C is a class or interface, the underlying object is destructed immediately via the .destroy function. This led to bugs when there are still references to the object outside of the Nullable instance:

    class C
    {
        int canary = 0xA71FE;
        ~this()
        {
            canary = 0x5050DEAD;
        }
    }
    
    auto c = new C;
    assert(c.canary == 0xA71FE);
    
    Nullable!C nc = nullable(c);
    nc.nullify;
    assert(c.canary == 0xA71FE); // This would fail
    

    The .nullify method has been fixed so that it no longer calls .destroy on class or interface instances, and the above code will now work correctly.

  4. std.algorithm.searching.substitute was added

    std.algorithm.searching.substitute yields a lazy range with all occurrences of substs in r replaced with their substitution:

    import std.algorithm.comparison : equal;
    import std.algorithm.searching : substitute;
    
    // substitute single elements
    assert("do_it".substitute('_', ' ').equal("do it"));
    
    // substitute multiple, single elements
    assert("do_it".substitute('_', ' ',
                               'd', 'g',
                               'i', 't',
                               't', 'o')
                  .equal("go to"));
    
    // substitute subranges
    assert("do_it".substitute("_", " ",
                              "do", "done")
                  .equal("done it"));
    
    // substitution works for any ElementType
    int[] x = [1, 2, 3];
    auto y = x.substitute(1, 0.1);
    assert(y.equal([0.1, 2, 3]));
    static assert(is(typeof(y.front) == double));
    

    If the substitution parameters are known at compile-time, the faster template overload can be used:

    import std.algorithm.comparison : equal;
    import std.algorithm.searching : substitute;
    
    // substitute subranges of a range
    assert("apple_tree".substitute!("apple", "banana",
                                    "tree", "shrub").equal("banana_shrub"));
    
    // substitute elements in a range
    assert("apple_tree".substitute!('a', 'b',
                                    't', 'f').equal("bpple_free"));
    
    // substitute values
    assert('a'.substitute!('a', 'b', 't', 'f') == 'b');
    
  5. divMod was added to std.bigint.

    std.bigint.divMod calculates both the quotient and the remainder in one go:

    void divMod(const BigInt dividend, const BigInt divisor, out BigInt quotient, out BigInt remainder) pure nothrow
    {
    auto a = BigInt(123);
    auto b = BigInt(25);
    BigInt q, r;
    
    divMod(a, b, q, r);
    
    assert(q == 4);
    assert(r == 23);
    assert(q * b + r == a);
    }
    
  6. getDigit Was Added To std.bigint

    std.bigint.getDigit gives the ulongs or uints that make up the underlying representation of the BigInt.

    import std.bigint;
    
    auto a = BigInt("1000");
    assert(a.getDigit(0) == 1000);
    
    auto b = BigInt("2_000_000_000_000_000_000_000_000_000");
    assert(b.getDigit(0) == 4584946418820579328);
    assert(b.getDigit(1) == 108420217);
    
  7. std.exception.enforce can now be used as an eponymous template to create your own enforce function

    std.exception.enforce now mirrors the behavior of std.exception.enforceEx and can be used as an eponymous template:

    import std.conv : ConvException;
    alias convEnforce = enforce!ConvException;
    assertNotThrown(convEnforce(true));
    assertThrown!ConvException(convEnforce(false, "blah"));
    

    With this change, std.exception.enforce is a strict superset of std.exception.enforceEx, which will be deprecated in 2.079.

  8. import std.experimental.all as a global convenience import

    std.experimental.all allows convenient use of all Phobos modules with one import:

    import std.experimental.all;
    void main()
    {
        10.iota.map!log.sum.writeln;
    }
    

    For short scripts a lot of imports are often needed to get all the modules from the standard library. With this release it's possible to use import std.experimental.all for importing the entire standard library at once. This can be used for fast prototyping or REPLs:

    import std.experimental.all;
    void main()
    {
        6.iota
          .filter!(a => a % 2) // 0 2 4
          .map!(a => a * 2) // 0 4 8
          .tee!writeln
          .sum
          .writefln!"Sum: %d"; // 18
    }
    

    As before, symbol conflicts will only arise if a symbol with collisions is used. In this case, static imports or renamed imports can be used to uniquely select a specific symbol.

    The baseline cost for import std.experimental.all is less than half a second (varying from system to system) and work is in progress to reduce this overhead even further.

  9. Replace std.experimental.allocator.IAllocator with std.experimental.allocator.RCIAllocator

    Motivation:

    Keep track of references to allocators so they don't escape, dangle, and cause undefined behavior.

    From now on, RCIAllocator will be used instead of the old IAllocator interface. std.experimental.allocator.allocatorObject can be used to build a RCIAllocator out of a custom allocator.

    import std.experimental.allocator.mallocator : Mallocator;
    
    RCIAllocator a = allocatorObject(Mallocator.instance);
    auto b = a.allocate(100);
    assert(b.length == 100);
    assert(a.deallocate(b));
    
  10. Replace std.experimental.allocator.ISharedAllocator with std.experimental.allocator.RCISharedAllocator

    Motivation:

    Keep track of references to allocators so they don't escape, dangle, and cause undefined behavior.

    From now on, RCISharedAllocator will be used instead of the old ISharedAllocator interface. std.experimental.allocator.sharedAllocatorObject` can be used to build a RCISharedAllocator out of a custom allocator.

    import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
    import std.experimental.allocator.mallocator : Mallocator;
    
    shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
    shared RCISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
    
    auto b = sharedFLObj.allocate(100);
    assert(b.length == 100);
    assert(sharedFLObj.deallocate(b));
    
  11. readText now checks BOMs

    std.file.readText now checks for a BOM. If a BOM is present and it is for UTF-8, UTF-16, or UTF-32, std.file.readText verifies that it matches the requested string type and the endianness of the machine, and if there is a mismatch, a std.utf.UTFException is thrown without bothering to validate the string.

    If there is no BOM, or if the BOM is not for UTF-8, UTF-16, or UTF-32, then the behavior is what it's always been, and UTF validation continues as per normal, so if the text isn't valid for the requested string type, a std.utf.UTFException will be thrown.

    In addition, before the buffer is cast to the requested string type, the alignment is checked (e.g. 5 bytes don't fit cleanly in an array of wchar or dchar), and a std.utf.UTFException is now throw if the number of bytes does not align with the requested string type. Previously, the alignment was not checked before casting, so if there was an alignment mismatch, the cast would throw an Error, killing the program.

  12. fold is added to std.parallelism.TaskPool

    std.parallelism.TaskPool.fold is functionally equivalent to TaskPool.reduce except the range parameter comes first and there is no need to use tuple for multiple seeds.

    static int adder(int a, int b)
    {
        return a + b;
    }
    static int multiplier(int a, int b)
    {
        return a * b;
    }
    
    // Just the range
    auto x = taskPool.fold!adder([1, 2, 3, 4]);
    assert(x == 10);
    
    // The range and the seeds (0 and 1 below; also note multiple
    // functions in this example)
    auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
    assert(y[0] == 10);
    assert(y[1] == 24);
    
    // The range, the seed (0), and the work unit size (20)
    auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
    assert(z == 10);
    
  13. nullSink was added to std.range

    std.range.nullSink is a convenience wrapper for std.range.NullSink and creates an output range that discards the data it receives. It's the range analog of /dev/null.

    import std.csv : csvNextToken;
    
    string line = "a,b,c";
    
    // ignore the first column
    line.csvNextToken(nullSink, ',', '"');
    line.popFront;
    
    // look at the second column
    Appender!string app;
    line.csvNextToken(app, ',', '"');
    assert(app.data == "b");
    
  14. std.range.slide (a fixed-size sliding window range) was added

    std.range.slide allows to iterate a range in sliding windows:

    import std.array : array;
    import std.algorithm.comparison : equal;
    
    assert([0, 1, 2, 3].slide(2).equal!equal(
        [[0, 1], [1, 2], [2, 3]]
    ));
    assert(5.iota.slide(3).equal!equal(
        [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
    ));
    
    assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]]));
    assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]]));
    
    // set a custom stepsize (default 1)
    assert(6.iota.slide(1, 2).equal!equal(
        [[0], [2], [4]]
    ));
    
    assert(6.iota.slide(2, 4).equal!equal(
        [[0, 1], [4, 5]]
    ));
    
    // allow slide with less elements than the window size
    assert(3.iota.slide!(No.withPartial)(4).empty);
    assert(3.iota.slide!(Yes.withPartial)(4).equal!equal(
        [[0, 1, 2]]
    ));
    
  15. std.string.strip now accepts a string of characters to be stripped

    New overload functions of std.string.strip, std.string.stripLeft and std.string.stripRight accepts a string of characters to be stripped instead of stripping only whitespace.

    import std.string: stripLeft, stripRight, strip;
    
    assert(stripLeft("www.dlang.org", "w.") == "dlang.org");
    assert(stripRight("dlang.org/", "/") == "dlang.org");
    assert(strip("www.dlang.org/", "w./") == "dlang.org");
    
  16. isSomeString and isNarrowString are now false for enums

    Previously, enums whose base type was a string type were true for std.traits.isSomeString and std.traits.isNarrowString. Occasionally, this was useful, but in general, it was a source of bugs, because code that works with strings does not necessarily work with enums whose base type is a string, making it easy to write code where an enum would pass the template constraint and then the template would fail to compile. For instance, enums of base type string are false for std.range.primitives.isInputRange. As such, it really doesn't make sense for std.traits.isSomeString and std.traits.isNarrowString to be true for enums.

    For some code, this will be a breaking change, but most code will either be unaffected, or it will then fail with enums at the template constraint instead of inside the function. So, the risk of code breakage is minimal but does exist. Other code will now be able to remove stuff like !is(T == enum) from its template constraints, since that is now part of std.traits.isSomeString and std.traits.isNarrowString, but it will continue to compile with the now unnecessary check for enums.

    Code that uses std.traits.isSomeString or std.traits.isNarrowString in a template constraint and has no other conditions which would prevent enums from passing the constraint but then would fail to compile inside the template if an enum were passed to it will now fail to compile at the template constraint instead of inside the template. So, such code is fixed rather than broken.

    The rare code that does break because of these changes is code that uses std.traits.isSomeString or std.traits.isNarrowString in a template constraint or static if and does not use other conditions to prevent enums from passing and actually has code in the template which compiles with both strings and enums with a base type of string. Such code will need to be changed to use std.traits.OriginalType, std.conv.asOriginalType, or std.traits.StringTypeOf instead. e.g. for enums to pass the constraint, instead of

    auto foo(S)(S str)
    if (isSomeString!S)
    {
        ...
    }
    

    the code would be

    auto foo(S)(S str)
    if (isSomeString!(OriginalType!S))
    {
        ...
    }
    

    As a rule of thumb, generic code should either disallow implicit conversions and force the caller to do the conversion explicitly (which generally is the least error-prone approach), or it should force the conversion to the desired type before the parameter is used in the function so that the function is definitely operating on the desired type and not just on one that implicitly converts to it and thus will work regardless of whether the argument was the exact type or implicitly converted to it.

    However, great care should be taken if the implicit conversion will result in slicing the parameter or otherwise referring to its address, since that makes it very easy for a reference to local memory to escape the function, whereas if the conversion is done at the call site, then the slicing is done at the call site, so the dynamic array is still valid when the function returns. Fortunately, enums with a base type of string do not have that problem, but other types which implicitly convert to dynamic arrays (such as static arrays) do have that problem, which is a big reason why it's generally recommended to not have generic functions accept types based on implicit conversions.

    It is recommended that if a function is supposed to accept both strings and types which implicitly convert to a string type, then it should explicitly accept dynamic arrays but be templated on the element type. e.g.

    auto foo(C)(C[] str)
    if (isSomeChar!C)
    {
        ...
    }
    

    That way, any implicit conversions are done at the call site and do not introduce @safety problems inside the function. It also tends to result in template constraints which are shorter and more easily understood.

  17. toString Can Now Use Output Ranges

    The standard library has been modified to recognize and use toString overloads that accept output ranges when such overloads exist.

    import std.range.primitives;
    import std.stdio;
    
    struct MyType
    {
        void toString(W)(ref W writer) if (isOutputRange!(W, char))
        {
            put(writer, "Custom toString");
        }
    }
    
    auto t = MyType();
    writeln(t); // writes "Custom toString"
    

    This has several benefits for the user. First, this design is much friendlier to inlining than the toString(scope void delegate(const(char)[]) sink) method of toString. Second, this cuts down on memory usage, as characters are placed right into the output buffers of functions like std.format.format. Third, because toString is now a template, can be marked @safe via inference much more often.

    All previous forms of toString will continue to work.


List of all bug fixes and enhancements in D 2.079.0:

DMD Compiler regressions

  1. Bugzilla 17605: [REG2.066.0] __traits(compiles, closure) adds link-time reference to _d_allocmemory
  2. Bugzilla 17970: shared struct destructor doesn't compile anymore
  3. Bugzilla 18030: Segmentation fault with __traits(getProtection) on template function.
  4. Bugzilla 18093: [Reg 2.071] MSCOFF: dmd crashes when overriding a C++ method in a mixin template
  5. Bugzilla 18097: [REG2.077] Unittest function is undefined identifier
  6. Bugzilla 18296: [Reg2.078.1] invalid code with coverage and copy construction
  7. Bugzilla 18322: void fun(string file=__FILE_FULL_PATH__)() returns relative path (pointing to nowhere)
  8. Bugzilla 18430: isSame is wrong for non global lambdas
  9. Bugzilla 18469: [REG 2.079-b1] Segfault when trying to get type of __dtor.opCall
  10. Bugzilla 18480: [Reg 2.079] dmd hangs with self-alias declaration

DMD Compiler bugs

  1. Bugzilla 8207: OS X: Should extern(D) symbols include another underscore?
  2. Bugzilla 8687: Variadic templates do not work properly with default arguments
  3. Bugzilla 9433: Deprecate delete
  4. Bugzilla 12511: static overloaded function is not accessible
  5. Bugzilla 12901: Assignments to outside members in in/out contracts shouldn't be allowed
  6. Bugzilla 14147: Compiler crash on identical functions in a single module
  7. Bugzilla 14907: DMD crash when using template name as a default value of template's typed argument
  8. Bugzilla 15777: Premature expansion of overload set in tuples
  9. Bugzilla 16042: Identifier on template arguments should consider eponymous member lookup
  10. Bugzilla 17437: Incorrect range check when implicitly converting integer literals to float/double
  11. Bugzilla 17570: Misleading error message illegal conditional function definition
  12. Bugzilla 17625: Confusing error message for private functions in different modules
  13. Bugzilla 17630: selective imports find symbols in private imports of other modules
  14. Bugzilla 17663: header generation (-H) is broken with public override of private default
  15. Bugzilla 18014: DMD test suite fails to link on Linux distros where PIC/PIE is enforced
  16. Bugzilla 18057: [ICE] Segmentation fault (stack overflow) in Expression::ctfeInterpret()
  17. Bugzilla 18083: -w doesn't work for the ddoc build
  18. Bugzilla 18143: in/out contracts should be implicitly 'this' const
  19. Bugzilla 18190: [asan] heap-buffer-overflow in Module.load.checkModFileAlias
  20. Bugzilla 18212: Usage of cfloat,cdouble,cfloat,ifloat,idouble,ireal shouldn't trigger an error in deprecated code
  21. Bugzilla 18218: __traits(isDeprecated, creal) should return true
  22. Bugzilla 18219: Private import inside struct leaks symbols when used as VarDeclaration types
  23. Bugzilla 18225: Wrong condition in VRP
  24. Bugzilla 18232: Union methods fail to initialize local variables to .init
  25. Bugzilla 18243: selective import + overload = private visibility
  26. Bugzilla 18261: Linkage information isn't part of the json output
  27. Bugzilla 18312: string concatenation with -betterC fails with linker errors
  28. Bugzilla 18335: The D_ObjectiveC version identifier is not printed in verbose mode
  29. Bugzilla 18364: header file generation doesn't print the package name in package(XXX)
  30. Bugzilla 18367: dmd should not segfault on -X with libraries, but no source files
  31. Bugzilla 18429: alias this enum causes segmentation fault
  32. Bugzilla 18468: cannot use synchronized {} in @safe code

DMD Compiler enhancements

  1. Bugzilla 6549: Implement contracts without implementation.
  2. Bugzilla 11529: Unclear error message when rvalue is passed as `ref'
  3. Bugzilla 11714: Improve error message for wrongly initialized thread-local class instances
  4. Bugzilla 13855: Allow multiple selective imports from different modules in a single import statement
  5. Bugzilla 16492: support @nogc in debug{} blocks
  6. Bugzilla 17899: Cannot initialise contextless delegate at compile time
  7. Bugzilla 18053: Use stdint.h mangling for int64_t/uint64_t when mangling D long/ulong
  8. Bugzilla 18149: Add a compiler trait to detect if a function is @disable
  9. Bugzilla 18273: Better C: wrong exit code from main()
  10. Bugzilla 18361: Ddoc: support ability to opt out of automatic keyword highlighting in text
  11. Bugzilla 18427: Symbol FOO is not visible because it is privately imported => compiler should show how to import it

Phobos regressions

  1. Bugzilla 18114: [Reg 2.078] regex performance regression
  2. Bugzilla 18316: std.net.curl.SMTP.mailTo fails to compile

Phobos bugs

  1. Bugzilla 7054: format() aligns using code units instead of graphemes
  2. Bugzilla 10879: std.variant Variant/Algebraic: Can't store static arrays > 32(/16) bytes
  3. Bugzilla 15157: std.experimental.allocator.building_blocks docs
  4. Bugzilla 15391: Problems loading libcurl.so and running datetime unittest on NixOS package build
  5. Bugzilla 17832: std.random.choice cannot be used with other random generators
  6. Bugzilla 18092: Can't combine take and takeExactly
  7. Bugzilla 18124: std.regex.RegexMatch's front property is under-documented
  8. Bugzilla 18153: deprecate public symbols ByLine, ByRecord, ByChunk
  9. Bugzilla 18224: BigInt modulo uint must return long.
  10. Bugzilla 18229: Misleading documentation of std.process.environment.get
  11. Bugzilla 18259: allocatorObject's CAllocatorImpl should store the passed allocator within
  12. Bugzilla 18278: writef documentation 404 error
  13. Bugzilla 18285: std.algorithm.comparison.cmp for strings with custom predicate compares lengths wrong
  14. Bugzilla 18286: std.algorithm.comparison.cmp for string with custom predicate fails if distinct chars can compare equal
  15. Bugzilla 18288: std.algorithm.comparison.cmp for wide strings should be @safe
  16. Bugzilla 18299: std.datetime.date.cmpTimeUnits does not throw a DateTimeException
  17. Bugzilla 18328: algorithm.startsWith can compare narrow string lengths in more circumstances
  18. Bugzilla 18349: std/math.d(543,33): Deprecation: integral promotion not done for -x
  19. Bugzilla 18384: std.net.isemail is slow to import due to regex
  20. Bugzilla 18397: Poor implementation of std.conv.hexString results in unintended bloat
  21. Bugzilla 18434: BigInt gcd asserts when one argument is zero.
  22. Bugzilla 18492: DLang STL links are broken

Phobos enhancements

  1. Bugzilla 5489: std.typecons tuples dynamically iterable
  2. Bugzilla 10828: datetime toString functions should accept sink
  3. Bugzilla 11084: std.algorithm.scan
  4. Bugzilla 11555: std.algorithm.reverse should return the just-reversed range
  5. Bugzilla 11747: Better error message with @disabled toString
  6. Bugzilla 13632: Second argument for std.string.strip
  7. Bugzilla 14767: Support CTFE of BigInt under x86
  8. Bugzilla 15949: Improve readtext handling of byte order mark (BOM)
  9. Bugzilla 17249: Make BigInt data visible (not modifiable!)
  10. Bugzilla 17440: Nullable.nullify() resets referenced object
  11. Bugzilla 18086: BigInt DivMod
  12. Bugzilla 18096: Add fold() to std.parallelism
  13. Bugzilla 18116: to!wchar([string, dstring]), and to!char([wstring, dstring]) don't compile
  14. Bugzilla 18152: std.format.formattedRead should work with rvalues.
  15. Bugzilla 18186: std.array.replaceSlice should be usable in @safe
  16. Bugzilla 18214: TemplateOf should return void for non-templated symbols
  17. Bugzilla 18217: Don't repeatedly call unpredictableSeed to initialize rndGen
  18. Bugzilla 18230: multiwayUnion sets wrong pred lambdas
  19. Bugzilla 18239: std.experimental.allocator fillWithMemcpy could use memset when T.sizeof==1
  20. Bugzilla 18280: std.algorithm.comparison.cmp for non-strings should call opCmp only once per item pair
  21. Bugzilla 18329: std.algorithm.startsWith & endsWith can sometimes statically determine decoding unnecessary

Druntime regressions

  1. Bugzilla 15482: new uuid.d forbids to link statically with other libraries

Druntime bugs

  1. Bugzilla 18240: core.stdc.wchar_ wmemset, etc. should be pure
  2. Bugzilla 18247: core.stdc.math functions that never set errno should be pure
  3. Bugzilla 18279: rt.util.utf does not properly reserve buffer in toUTF16/toUTF16z
  4. Bugzilla 18300: core.demangle demangling of really long symbol fails
  5. Bugzilla 18409: DScanner SEGFAULTS on CircleCI
  6. Bugzilla 18531: core.exception.RangeError@src/core/demangle.d(216): Range violation

dlang.org bugs

  1. Bugzilla 13844: core.stdc.config isn't listed in the docs
  2. Bugzilla 14475: man page is outdated
  3. Bugzilla 16490: Usage of attributes in inline asm blocks is not documented
  4. Bugzilla 18306: No compliation errors shown when running modified examples
  5. Bugzilla 18319: std.exception: enforce example does not compile
  6. Bugzilla 18341: Documentation for std.array.split is confusing/incorrect
  7. Bugzilla 18355: [Areas of D usage]

dlang.org enhancements

  1. Bugzilla 17998: Document Options for install.sh
  2. Bugzilla 18202: Show TOC overview in the dlang specification pages
  3. Bugzilla 18337: https://dlang.org/spec/operatoroverloading.html missing opIn/opIn_r
  4. Bugzilla 18379: [404 Not Found] Foundation Donate page not found
  5. Bugzilla 18383: Front page blog section is only partially filled.

Tools bugs

  1. Bugzilla 18208: demangle RangeError@src/core/demangle.d(230)

Installer bugs

  1. Bugzilla 15131: curl.lib is not available in 32 bit mscoff format
  2. Bugzilla 18510: [Beta 2.079] lld-link.exe fails to open obj file in subpath

Contributors to this release (78)

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

previous version: 2.078.3 – next version: 2.079.1