Language Changes

Const and immutable fields with initializers are now warned about: Eventually, they will be deprecated, and then will be an error. Such fields should now be changed to enum, static or immutable. Eventually, new behavior for them will be enabled, as follows: Fields in an aggregate which are not static will now always be addressable. This means they occupy space in the object:

struct S { immutable int [] arr = [1, 2]; const int [] arr2 = [1, 2]; }

This means that code which accessed such declarations without the this reference will no longer compile. Additionally code which depended on the size of a structure with such fields will have to be fixed:

struct S { immutable int [] arr = [1, 2]; } void main() { auto x = S.arr; static assert (S.sizeof == size_t.sizeof + size_t.sizeof); }

To make the field static again, simply use the static keyword. Alternatively make the field an enum to turn it into a manifest constant:

struct S { static immutable int [] arr = [1, 2]; enum arr2 = [1, 2]; }

Note however that manifest constants which are arrays are allocated on each usage, so you may prefer using static instead.

Rationale:

Making a field implicitly static based on whether it is const/immutable and has an initializer leads to confusion. The static keyword can be used to explicitly make any field static.

A qualified constructor is now invoked when a const/immutable/shared aggregate object is instantiated, respectively:

import std.stdio; class C { this () { writeln( "1" ); } this () const { writeln( "2" ); } this () immutable { writeln( "3" ); } this () shared { writeln( "4" ); } } void main() { auto a = new C; auto b = new const C; auto c = new immutable C; auto d = new shared C; }

This has the consequence that aggregates which have only immutable or shared constructors can no longer be used to instantiate mutable objects:

class C { this () immutable { } this () shared { } } void main() { auto c1 = new C; auto c2 = new immutable C; auto c3 = new shared C; }

On the other hand, aggregates which do not have shared or immutable constructors can no longer be used to construct shared or immutable objects, respectively:

class C { this () { } } void main() { auto c1 = new C; auto c2 = new immutable C; auto c3 = new shared C; }

However, if an aggregate has a pure constructor it can be used to construct an object with any type constructor:

class C { this () pure { } } void main() { auto c1 = new C; auto c2 = new immutable C; auto c3 = new shared C; }

In earlier releases some struct members such as arrays would be bitwise-compared in a comparison operation. This has now been changed to be a structural comparison instead:

struct S { char [] data; } void main () { auto s1 = S( "foo" .dup); auto s2 = S( "foo" .dup); assert (s1.data ! is s2.data); assert (s1 == s2); assert (s1.data == s2.data); }

If an opEquals function is not present the compiler rewrites the expression s1 == s2 to s1.tupleof == s2.tupleof. Comparing .tupleof expressions is also a feature new to D in the 2.063 release.

The right-hand-side of an array copy operation now requires using the slice syntax:

void main() { int [][2] x; int [] y; int [] z; x[] = z; y[] = z; }

If the user intended to write such code they must use the slice syntax for both the source and target arrays:

void main() { int [][2] x; int [] y; int [] z; y[] = z[]; }

Rationale:

The compiler will emit a warning to make the user aware that the copy operation is arbitrarily expensive.

A type can no longer be passed to a function as a value of that type:

T[] foo(T)(T t) { return null ; } void main() { alias int Int; alias typeof (foo(Int)) IntArray; }

If the user wants to pass an argument of a certain type, they can use the .init property:

T[] foo(T)(T t) { return null ; } void main() { alias typeof (foo( int .init)) IntArray; }

Rationale:

Treating types as expressions in special contexts only leads to confusion. Instead, the .init property can be used for such purposes.

The index variable in a foreach range is now by default a value type:

void main() { size_t count; foreach (n; 0 .. 10) { ++n; ++count; } assert (count == 10); }

If the user wants to modify the index variable he must use the ref keyword:

void main() { size_t count; foreach ( ref n; 0 .. 10) { ++n; ++count; } assert (count == 5); }

Rationale:

Making the index variable implicitly ref can introduce bugs that are hard to track down.

An associative array entry used to be default-initialized before assignment took place:

void main() { int [ int ] aa; aa[1] = aa[1] + 1; assert (aa[1] == 1); }

In 2.063, accessing an entry which does not exist will now throw a RangeError:

void main() { int [ int ] aa; aa[1] = aa[1] + 1; }

Rationale:

Default-initialization during assignment can be a source of bugs.

Method overrides no longer inherit constness of the base method:

class A { void foo() const { } } class B : A { override void foo() { } }

If the user wants to override a const method he has to mark the overriden method as const:

class A { void foo() const { } } class B : A { override void foo() const { } }

The feature allows introducing new overloads based on the constness of the method:

class A { void foo() const { } } class B : A { void foo() { } alias super .foo foo; override void foo() const { } }

The following code used to be allowed:

void f( int [] function () del) { assert (!del()); } typeof ( null ) g() { return null ; } void main() { f(&g); f(() => null ); }

However the implicit conversion would end up generating wrong code. To work around this, make sure the return type is typed properly, or use (T[]).init in the return expression of a lambda expression:

void f( int [] function () del) { assert (!del()); } int [] g() { return null ; } void main() { f(&g); f(() => ( int []).init); }

The Template This Parameter can now be used to infer the qualifier of this to member functions:

struct S { void foo( this T)() { } } void main() { immutable S s; s.foo(); }

Array slices are no longer l-values. This means an address can no longer be taken of a slice, and slices cannot be passed by ref to functions:

void foo( ref int [] arr) { arr = new int [10]; } void main() { int [] arr; foo(arr); assert (arr.length == 10); foo(arr[]); auto ptr = &arr[1..2]; }

To work around this you can make your function take an r-value if it doesn't need to reassign and resize the slice, but only needs to read or modify its contents. Otherwise, to accept both l-values and r-values you can make your function take its argument by `auto ref`:

void take( int [] arr) { } void takeRef( ref int [] arr) { } void takeAutoRef(T)( auto ref T[] arr) { } void main() { int [] arr = [1, 2, 3, 4]; take(arr); takeRef(arr); takeAutoRef(arr); int [] arr2 = arr[1 .. 2]; take(arr2); takeRef(arr2); takeAutoRef(arr2); take(arr[1 .. 2]); takeRef(arr[1 .. 2]); takeAutoRef(arr[1 .. 2]); }

Rationale:

Passing slices by reference had no observable effect when reassigning or resizing such a slice at the call site, therefore such slices should by default be r-values. For example, the following code used to be allowed but is now a compile-time error:

void reAssign( ref int [] arr) { arr = new int [2]; } void reSize( ref int [] arr) { arr.length = 10; } void main() { int [] arr = [1, 2, 3, 4]; reAssign(arr[0 .. 4]); assert (arr == [1, 2, 3, 4]); reSize(arr[0 .. 4]); assert (arr.length == 4); }

Accessing non-static fields used to be allowed in many contexts, but is now limited to only a few:

- offsetof, init, and other built-in properties are allowed:

struct S { int field; } void main() { auto a = S.field.offsetof; auto c = S.field.max; auto d = S.field; }

- When invoking static methods of a non-static field:

struct Foo { static struct Bar { static int get() { return 0; } } Bar bar; } void main() { static assert (Foo.bar.get() == 0); }

- When accessing static fields implicitly using an alias this expression:

struct Foo { static struct Bar { static int get() { return 0; } } Bar bar; alias bar this ; } void main() { static assert (Foo.get() == 0); }

The implicit conversion of an array to a pointer was a deprecated feature:

void foo( int * p) { } void main() { int [] arr = [1, 2]; foo(arr); }

This feature has now been completely removed. The workaround is to either use the .ptr property, or explicitly pass the pointer to the first element: