Legacy Code
To maintain compatibility with older D code, many legacy features remain supported. This page describes each legacy feature that is supported, with a suggestion of how to modernize the code.
| Feature | Summary | Edition check |
|---|---|---|
| body keyword | body after a contract statement - use do instead | 2024 |
| alias target name; syntax | use alias name = target; instead | 2024 |
| Aliasing an instance member | Use typeof(instance).member instead | 2024 |
| Escaping scope data | scope is enforced in @safe code | 2024 |
| Assigning to struct rvalue | Disallow for structs which overload e.g. opAssign | 2024 |
| Struct/union postblit | use a copy constructor instead. |
body keyword
body was a keyword used to specify a function/method's body after a contract statement:
class Foo { void bar(int i) in { assert(i >= 42); } body { /* Do something interesting */ } string method(string s) out(v) { assert(v.length == s.length); } body { /* Do something even more interesting */ } void noBody() { /* No contracts, no body */ } }
Corrective Action
Use the do keyword instead (introduced in v2.075.0):
void bar(int i) in { assert(i >= 42); } do { /* Look ma, no body! */ }
alias target name; syntax
From the 2024 edition, AliasDeclaration only supports alias name = target; syntax. This is easier to find the symbol identifier, particularly when target is a complex symbol. The target first order came from C's typedef syntax.
Aliasing an instance member
E.g. alias a = instance.field;. Such an alias actually aliases a member of the instance's type, not the instance member itself. That could be confusing. Instead, alias a member of the type.
struct Bar { Foo f; alias v = f.v; // Error, use `typeof(f).v` } 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); } }
Assigning to struct rvalue
It has always been an error for POD structs to assign from an rvalue.
From the 2024 edition, it is 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 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.
Corrective Action
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.