Change Log: 2.114.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
Compiler changes
- Shared static constructors/destructors in templates now lower to object._d_atomicOp
- Coverage counters are now emitted at call sites of inlined functions
- Added @__ctfe attribute for compile-time function enforcement
- Many deprections have been turned in errors
- The fast DFA engine has gained escape analysis capabilities
- Foreach variable shadowing is now an error
- -ftime-trace: add instrumentation for the inliner pass
- -ftime-trace: break up the template instance span into sub-phases
- ImportC supports single-argument _Static_assert(expr)
- ImportC pragmas for C construct consideration
- The Object.__monitor field has been moved to druntime
- WASIp1, WASIp2, and WASIp3 are now reserved version identifiers
- Implicit Function Template Instantiation (IFTI) handled named arguments better
- Improved CodeView / PDB symbolic debug info on Windows
- Token strings now normalize \r\n to \n
- Disallow discarding an assignment to a struct rvalue
- Tuple unpacking is now supported!
- UCRT is now used for the C runtime on Windows
Runtime changes
Library changes
Dub changes
List of all upcoming bug fixes and enhancements in D 2.114.0.
Compiler changes
- Shared static constructors/destructors in templates now lower to object._d_atomicOp
When a shared static constructor or destructor appears inside a template, it may be instantiated in multiple modules, causing it to run multiple times. A shared gate variable is used to count and guard executions, ensuring the body runs exactly once across all modules. The atomic operation on that gate is necessary because multiple threads may initialize modules concurrently.
This gate operation previously called core.atomic.atomicOp directly.
shared static this() { if (atomicOp!"+="(gate, 1) != 1) return; // inside a template instantiation }
It now lowers to object._d_atomicOp, consistent with how other compiler-generated runtime hooks are handled.
if (.object._d_atomicOp!"+="(gate, 1) != 1) return;
- Coverage counters are now emitted at call sites of inlined functions
Functions marked pragma(inline, true) (and functions inlined via the -inline flag) would always show 0000000 in -cov coverage reports even when executed. The inlined function body is never called as a standalone function at runtime - only the inlined copy inside the caller runs, so its coverage counters stayed at zero.
Coverage counters are now emitted at each call site where a function is inlined, so the function's lines correctly reflect how many times they were executed.
Fixes Issue 5848 / #18337
- Added @__ctfe attribute for compile-time function enforcement
A new @__ctfe attribute has been added to mark functions that can only be executed at compile time. Functions marked with @__ctfe cannot be called from runtime code and will not generate any object code.
When a @__ctfe function is used in a runtime context, the compiler will emit an error:
int add(int a) @__ctfe => a + 2; void main() { int x = add(9); // Error: function add marked with @__ctfe cannot be called at runtime }
The attribute is useful for defining helper functions that should only be evaluated during compilation, ensuring they don't contribute to binary size or runtime overhead:
@__ctfe int square(int x) => x * x; enum Result = square(5); // OK - evaluated at compile time static assert(Result == 25);
Taking the address of a @__ctfe function in runtime code is also an error:
int add(int a) @__ctfe => a + 2; void main() { auto fp = &add; // Error: cannot take address of function marked with @__ctfe }
- Many deprections have been turned in errors
Using return statements in scope guards is now an error (in addition to contracts and try...finally)
- The fast DFA engine has gained escape analysis capabilities
The engine works on the fundamental concept on "cells", a cell is a location in memory that holds something. So a variable that is on the stack has a cell that provides it, but also may point to another cell if its typed as a pointer.
Due to the fast DFA engine not being able to model indirection, the cell of a variable and its containing pointer is considered separately from cells seen via indirection. This is particularly interesting with how by-ref parameters handle it. The cell pointed at by the by-ref is the outer cell, even if its typed as a pointer.
// Think of parameter as int* not int, so the outer cell is the container for the int. int* pointToRefCell(ref int arg) => &arg;
This allows you to escape values that come from indirection:
struct Animal { int* datem; } int* grabFromAnimal(scope Animal* animal) => animal.datem;
Partial violations of scope is allowed in non-@safe functions. Escaping via a throw statement will still error.
The first 29 parameters may be treated as outputs, all others must be inputs only. This does not include the this pointer.
No attributes have been added at this time for users to use, you must rely solely on existing ones and inference.
Experimentally it may promote stack variables that are typed as a class to stack automatically. This may be disabled in the future if it is shown to cause problems.
- Foreach variable shadowing is now an error
A deprecation introduced in DMD 2.089.0 for foreach variables shadowing outer scope symbols has been upgraded to an error.
This affects both variables declared inside a foreach body that shadow outer variables, and duplicate variable names in foreach parameter lists when using opApply.
void main() { int[int] arr; int x; foreach (i, j; arr) { int x; // Error: variable `x` is shadowing variable `...x` } }
struct Foo { int opApply(scope int delegate(size_t, size_t, ref uint)) => 0; } void main() { foreach (x, y, x; Foo()) {} // Error: variable `x` is shadowing variable `...x` }
- -ftime-trace: add instrumentation for the inliner pass
The -ftime-trace output previously had no coverage for the inliner pass. When compiling with -inline, the time spent scanning and inlining functions did not appear in the trace at all.
Two new spans are now emitted: Inlining covers the entire inliner pass, and Inline: <function> covers each function scanned individually. These are visible in trace viewers like Perfetto.
- -ftime-trace: break up the template instance span into sub-phases
The -ftime-trace output previously showed template instantiation as a single flat block, making it hard to tell where the time was actually going.
The Sema1: Template Instance span now contains sub-spans for each phase: Sema1: Template Arg Semantic, Sema1: Overload Resolution, Sema1: Template Members, Sema2: Template Instance, and Sema3: Template Instance. These are visible in trace viewers like Perfetto.
- ImportC supports single-argument _Static_assert(expr)
C23 makes the message operand of a static assertion optional. ImportC now accepts both forms:
_Static_assert(sizeof(int) == 4); _Static_assert(sizeof(int) == 4, "unexpected int size");
The two-argument form continues to work as before. When the assertion fails and no message was supplied, the diagnostic reports that the condition is false.
- ImportC pragmas for C construct consideration
ImportC now has a number of pragmas which may be used to alter whether ImportC considers or ignores declarations and definitions with specific identifiers.
Those pragmas are:
- #pragma function_decl(ignore, <identifier>,...)
- #pragma function_decl(consider, <identifier>,...)
- #pragma function_def(ignore, <identifier>,...)
- #pragma function_def(consider, <identifier>,...)
These pragmas are for situations where it is desirable for ImportC to look past a C declaration/definition of a symbol, and to instead use a declaration/definition from an __imported D module.
For example, a small subset of a C library's functions may rely on non-standard compiler extensions that are unsupported by ImportC; in such a situation #pragma function_def(ignore, <identifier>,...) can be used to ignore the offending functions so that they can instead be implemented in D and made available to ImportC via an __imported D module.
Likewise, a C library may rely on externally-defined functions which are not defined when compiling via ImportC; #pragma function_decl(ignore, <identifier>,...) can be used to ignore the function declarations and those functions can instead be implemented in D and __imported in C.
The Construct Consideration pragmas are used as follows:
- #pragma function_decl(ignore, <identifier>,...)
- #pragma function_decl(consider, <identifier>,...)
- #pragma function_def(ignore, <identifier>,...)
- #pragma function_def(consider, <identifier>,...)
#pragma function_decl is used to alter whether ImportC ignores or considers (stops ignoring) function declarations with the same name as any of the identifiers supplied to the pragma.
#pragma function_def is used to alter whether ImportC ignores or considers (stops ignoring) function definitions with the same name as any of the identifiers supplied to the pragma.
For example:
// We start ignoring function-declarations and function-definitions of `foo` and `bar`. #pragma function_decl(ignore, foo, bar) #pragma function_def(ignore, foo, bar) // This declaration of `foo` is ignored. void foo(int); // As is this definition of `foo`. void foo(int x) {} // We stop ignoring function-definitions of `bar`. #pragma function_def(consider, bar) // This declaration of `bar` is ignored. void bar(int); // This definition of `bar` is not ignored. float bar(float x, float y) { return x * y; } - The Object.__monitor field has been moved to druntime
This field was previously hard coded into the compiler, and is used as a mutex for synchronized(obj) blocks. By declaring it explicitly in druntime's Object, custom druntimes can now omit it entirely, saving 8 bytes per class instance when synchronized is not needed.
The field is treated specially by the compiler and remains excluded from:
- __traits(allMembers, Object)
- Object.tupleof
- __traits(getPointerBitmap, T) (it is not GC-managed)
- WASIp1, WASIp2, and WASIp3 are now reserved version identifiers
New version identifiers are reserved for WASI, corresponding to previews 1, 2, and 3 of the WebAssembly System Interface.
The existing WASI identifier is retained, meaning ANY version of WASI. i.e. WASI is always defined whenever WASIp{1|2|3} is.
- Implicit Function Template Instantiation (IFTI) handled named arguments better
https://github.com/dlang/dmd/issues/21335 https://github.com/dlang/dmd/issues/22878
When calling a function template with named arguments, parameters that are skipped (because their named argument was not provided) now correctly use their template parameter defaults for type deduction.
void f(A = int, B = int)(A a = A.init, B b = B.init) {} void main() { f(b: "hello"); // A = int (template default), B = string (deduced) }
Named arguments that appear behind variadic arguments can now be assigned to:
void error(T...)(T args, string file = __FILE__, int line = __LINE__) {} auto text(T...)(T args, Allocator alloc) {} void foo(Allocator gc) { error("Code: ", code, file: __FILE__, line: __LINE__) return text("hello", "world", alloc: gc); }
- Improved CodeView / PDB symbolic debug info on Windows
The Windows CodeView / PDB debug information emitter (used with -g when producing MS-COFF object files) now emits additional modern records expected by Microsoft and LLVM debugging tools. These improvements are enabled by default, so debugging with Visual Studio, WinDbg and LLVM-based tools works better out of the box.
The following are now generated:
- S_OBJNAME and S_COMPILE3 compiland records reporting the configured language and the real compiler version string,
- S_ENVBLOCK and S_BUILDINFO (LF_BUILDINFO) records describing the build environment,
- S_FRAMEPROC records describing each function's stack frame,
- LF_UDT_SRC_LINE records recording the source file and line where each user-defined type (struct, class, enum) is defined, so debuggers can jump to a type's definition,
- blake3 source-file checksums in the file-checksums subsection so debuggers can verify the source matches.
No action is required; simply compile with -g as before:
dmd -g myapp.d
- Token strings now normalize \r\n to \n
Token strings (q{...}) now normalize carriage return + line feed sequences to a single line feed, consistent with other string literal types.
const string s = q{a b}; // file saved with CRLF line endings static assert(s == "a\nb");
- Disallow discarding an assignment to a struct rvalue
It has always been an error for POD structs to assign from an rvalue. It is now an error to discard the result of an assignment from a struct rvalue when it would call opAssign, opOpAssign, opUnary!"++", or opUnary!"--" and the struct has no tail-mutable pointer fields:
struct S { int i; void opAssign(S s); } S foo(); void main() { foo() = S(2); // Error, possible no-op }
Above, unless opAssign mutates global data, the assignment in main will have no effect and indicates a bug.
If a struct rvalue assignment is needed to mutate global state, either call the operator overload method directly or use an lvalue. Note: Calling a non-const method on a struct rvalue is allowed.
- Tuple unpacking is now supported!
D now supports tuple unpacking for variable declarations, foreach loops, and function literal template parameters. This feature is available as a preview and can be enabled with the -preview=tuples compiler switch. It allows extracting multiple values from a tuple or compile-time sequence directly into distinct variables.
// Requires -preview=tuples import std.typecons : tuple; void main() { auto (x, y, z) = tuple(1, 2.5, "three"); assert(x == 1); assert(y == 2.5); assert(z == "three"); // Types can be specified explicitly (int a, string b) = tuple(4, "five"); // Unpacking works in foreach loops foreach ((i, s); [tuple(1, "one"), tuple(2, "two")]) { // ... } }
For more information, see the D blog article: DIP 1053 - A Tale of Tuples.
- UCRT is now used for the C runtime on Windows
DMD on Windows now uses the Universal CRT (UCRT) for its C runtime in all cases.
For installations with Visual Studio 2015 (or later) or the Windows 10 SDK, the default C runtime is unchanged: it remains libcmt, which statically links the UCRT.
When no such Visual C installation is found, DMD now falls back on the UCRT-based libraries bundled in the MinGW folder shipped with DMD (ucrtbase.lib together with vcruntime140.lib), instead of the old msvcrt120 runtime which did not support modern format specifiers such as %zd. This fallback is fully supported.
If no UCRT-capable toolchain is detected and no -mscrtlib switch is given, DMD now reports a clear error instead of silently producing a non-working link.
Detection of Visual Studio versions older than 2015 (2008 - 2013) is deprecated, because those versions predate the UCRT. Falling back on such an installation emits a deprecation warning.
The -mscrtlib switch is unchanged and can still be used to select any C runtime, including msvcrt (dynamic), the debug variants libcmtd / msvcrtd, or a legacy msvcrtNNN runtime.
Runtime changes
- ImportC implements the vast majority of MSVC's intrinsics: 461 of them to be exact.
ImportC now implements the vast majority of MSVC's intrinsics: 461 of them to be exact. All but three of the intrinsics listed here are implemented, alongside a handful of undocumented intrinsics, accompanied by a smattering of ISA-specific intrinsics.
The presence of these intrinsics greatly improves ImportC's ability to successfully import C headers when targeting Windows. Notably, Windows.h can now be successfully included by ImportC.
To make use of MSVC intrinsics in ImportC, simply #include the importc_msvc_builtins.h header before any C code that uses MSVC intrinsics:
#include <importc_msvc_builtins.h> #include <windows.h>
MSVC intrinsics are available when targeting the Microsoft C runtime (that is, when the CRuntime_Microsoft version identifier is defined).
Every intrinsic implemented has received optimised code for LDC, GDC, and DMD. Each intrinsic, where possible, has a CTFE-compatible code path.
(Owing to the newness of DMD's AArch64 backend, MSVC intrinsics have not yet been implemented for DMD AArch64 targets. They have been implemented for LDC and GDC AArch64 targets.)
- Translate ^^ operator to druntime hook
The exponentiation operator (^^) is now lowered to a druntime hook call instead of being expanded inline by the compiler. This change moves the implementation logic into the runtime library, allowing for easier maintenance and platform-specific optimizations without requiring compiler modifications.
auto _d_pow(Base, Exp)(Base base, Exp exp); auto _d_sqrt(T)(T x);
- core.sys.linux.timerfd moved to core.sys.linux.sys.timerfd
Usually, a linux header that's included in C like #include <sys/time.h> gets its translation in core/sys/linux/sys/time.d. timerfd.d was an exception, not being put in the sys package. The module has been moved there, and the old module still exists as a deprecated module that publicly imports the new module.
import core.sys.linux.timerfd; // deprecated import core.sys.linux.sys.timerfd; // corrective action
Library changes
- New GCHeapMallocator allocator in std.experimental.allocator
GCHeapMallocator allocates from the D runtime's garbage-collected heap, but expects the memory to be released manually via deallocate instead of leaving it for the collector. The memory is still scanned for pointers by the GC.
It offers the same allocation, reallocation and deallocation behaviour that GCAllocator has today, under a name that says so explicitly.
import std.experimental.allocator.gc_allocator : GCHeapMallocator; auto buffer = GCHeapMallocator.instance.allocate(1024 * 1024 * 4); scope(exit) GCHeapMallocator.instance.deallocate(buffer);
- getopt: add callback handler with element index
This adds a new callback handler variant to getopt. The handler is of the form (string option, string value, size_t index). The index is the position of the currently processed option in the argument array passed to getopt. This enables getopt to handle positional arguments.
- Language dependent casing rules are now implemented in std.uni
The functions toLowerSpecial and toUpperSpecial provide case conversion when a language Lithuanian, Turkish or Azeri is provided to it that follows that languages rules.
This solves the problem of trying to make the letter i upper case into the Turkish İ.
Dub changes
- Fixed --conf options not being passed to LDC's linker step.
Packages can now use --conf as a dflag to modify LDC's linker behavior for a package.
Example:
dflags "--conf=$KON_PACKAGE_DIR/55-wasm-wasi.conf" platform="wasm32"
- Added dub generate ninja command.
Introduces a Ninja build backend for DUB. Running dub generate ninja produces a build.ninja file with compile and link rules for all source files in the project, using the correct flag syntax for DMD, LDC, and GDC.
- Fix Windows response file quoting for paths with trailing backslashes
When dub writes spaced compiler flags into @ response files, a trailing directory separator before the closing quote could escape that quote under DMD/LDC response expansion. This broke ldc2 builds for users whose dub cache paths contain spaces (regression since import paths gained trailing slashes).
Related: ldc-developers/ldc#5134
List of all bug fixes and enhancements in D 2.114.0: