Expressions - D Programming Language
Description: D Programming Language
Keywords: D programming language

Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Forums
  - IRC
  - Community Discord
  - Wiki
  - Issues
  - Contributing
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

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

# Expressions

Contents

1. Expressions
2. Definitions and Terms
   1. Full Expression
   2. Lvalue
   3. Rvalue
   4. Smallest Short-Circuit Expression
3. Order Of Evaluation
   1. Increment and Decrement
   2. Binary Expressions
   3. Conditional Expressions
   4. Function Calls
4. Lifetime of Temporaries
5. Comma Expression
6. Assign Expressions
   1. Simple Assignment Expression
   2. Assignment Operator Expressions
7. Conditional Expressions
8. Logical Expressions
   1. OrOr Expressions
   2. AndAnd Expressions
9. Bitwise Expressions
   1. Or Expressions
   2. Xor Expressions
   3. And Expressions
10. Compare Expressions
    1. Equality Expressions
    2. Identity Expressions
    3. Relational Expressions
    4. Array Comparisons
    5. Integer Comparisons
    6. Floating Point Comparisons
    7. Class and Struct Comparisons
11. In Expressions
12. Shift Expressions
13. Additive Expressions
    1. Add Expressions
    2. Cat Expressions
14. Mul Expressions
    1. Division
    2. Floating Point
15. Unary Expressions
    1. Complement Expressions
    2. Delete Expressions
    3. Cast Expressions
16. Throw Expression
17. Pow Expressions
18. Postfix Expressions
    1. Postfix Argument Lists
    2. Index Operations
    3. Slice Operations
19. Primary Expressions
    1. this
    2. super
    3. null
    4. String Literals
    5. Hex String Literals
    6. Array Literals
    7. Associative Array Literals
    8. Function Literals
    9. Uniform construction syntax for built-in scalar types
    10. Assert Expressions
    11. Mixin Expressions
    12. Import Expressions
    13. New Expressions
    14. Typeid Expressions
    15. Is Expressions
    16. Rvalue Expression
    17. Special Keywords
20. Associativity and Commutativity

## Expressions

    Expression:
        CommaExpression

An expression is a sequence of operators and operands that specifies an evaluation. The syntax, order of evaluation, and semantics of expressions are as follows.

Expressions are used to compute values with a resulting type. These values can then be assigned, tested, or ignored. Expressions can also have side effects.

## Definitions and Terms

### Full Expression

For any expression expr, the full expression of expr is defined as follows. If expr parses as a subexpression of another expression expr1, then the full expression of expr is the full expression of expr1. Otherwise, expr is its own full expression.

Each expression has a unique full expression. Example:

    return f() + g() * 2;

The full expression of g() * 2 above is f() + g() * 2, but not the full expression of f() + g() because the latter is not parsed as a subexpression.

Note: Although the definition is straightforward, a few subtleties exist related to function literals:

    return (() => x + f())() * g();

The full expression of f() above is x + f(), not the expression passed to return. This is because the parent of x + f() has function literal type, not expression type.

### Lvalue

The following expressions, and no others, are called lvalue expressions or lvalues:

1. this inside struct and union member functions;
2. a variable, function name, or invocation of a function that returns by reference;
3. the result of the . PostfixExpression and Module Scope Operator when the rightmost side of the dot is a variable, field (direct or static), function name, or invocation of a function that returns by reference;
4. the result of the following expressions:
   - built-in unary operators + (when applied to an lvalue), *, ++ (prefix only), -- (prefix only);
   - built-in indexing operator [] (but not the slicing operator);
   - built-in assignment operators, i.e. =, +=, *=, /=, %=, &=, |=, ^=, ~=, <<=, >>=, >>>=, and ^^=;
   - user-defined operators if and only if the function called as a result of lowering returns by reference;
   - the ConditionalExpression operator e ? e1 : e2 under the following circumstances:
   1. e1 and e2 are lvalues of the same type; OR
   2. One of e1 and e2 is an lvalue of type T and the other has an alias this which converts it to an lvalue of T;
   - mixin expressions if and only if the compilation of the expression resulting from compiling the argument(s) to mixin is an lvalue;
   - cast(U) expressions applied to lvalues of type T when T* is implicitly convertible to U*;
   - cast(TypeCtorsopt) when applied to an lvalue.

### Rvalue

Expressions that are not lvalues are rvalues. Rvalues include all literals, special value keywords such as __FILE__ and __LINE__, enum values, and the result of expressions not defined as lvalues above.

The built-in address-of operator (unary &) may only be applied to lvalues.

### Smallest Short-Circuit Expression

Given an expression expr that is a subexpression of a full expression fullexpr, the smallest short-circuit expression, if any, is the shortest subexpression scexpr of fullexpr that is an AndAndExpression (&&) or an OrOrExpression (||), such that expr is a subexpression of scexpr. Example:

    ((f() * 2 && g()) + 1) || h()

The smallest short-circuit expression of the subexpression f() * 2 above is f() * 2 && g(). Example:

    (f() && g()) + h()

The subexpression h() above has no smallest short-circuit expression.

## Order Of Evaluation

Best Practices: Even when the order of evaluation is well-defined, writing code that depends on it is rarely recommended.

### Increment and Decrement

Built-in prefix unary expressions ++ and -- are evaluated as if lowered (rewritten) to assignments as follows:

Expression | Equivalent
--- | ---
++expr | ((expr) += 1)
--expr | ((expr) -= 1)

Therefore, the result of prefix ++ and -- is the lvalue after the side effect has been effected.

Built-in postfix unary expressions ++ and -- are evaluated as if lowered (rewritten) to lambda invocations as follows:

Expression | Equivalent
--- | ---
expr++ | (ref x){auto t = x; ++x; return t;}(expr)
expr-- | (ref x){auto t = x; --x; return t;}(expr)

Therefore, the result of postfix ++ and -- is an rvalue just before the side effect has been effected.

    int i = 0;
    assert(++i == 1);
    assert(i++ == 1);
    assert(i == 2);

    int* p = [1, 2].ptr;
    assert(*p++ == 1);
    assert(*p == 2);

### Binary Expressions

Binary expressions except for AssignExpression, OrOrExpression, and AndAndExpression are evaluated in lexical order (left-to-right). Example:

    int i = 2;
    i = ++i * i++ + i;
    assert(i == 3 * 3 + 4);

OrOrExpression and AndAndExpression evaluate their left-hand side argument first. Then, OrOrExpression evaluates its right-hand side if and only if its left-hand side does not evaluate to nonzero. AndAndExpression evaluates its right-hand side if and only if its left-hand side evaluates to nonzero.

Implementation Defined: The order of evaluation of the operands of AssignExpression.

### Conditional Expressions

ConditionalExpression evaluates its left-hand side argument first. Then, if the result is nonzero, the second operand is evaluated. Otherwise, the third operand is evaluated.

### Function Calls

Calls to functions with extern(D) linkage (which is the default linkage) are evaluated in the following order:

1. If necessary, the address of the function to call is evaluated (e.g. in the case of a computed function pointer or delegate).
2. Arguments are evaluated left to right.
3. Transfer of execution is passed to the function.

Example calling a function pointer:

    void function(int a, int b, int c) fun()
    {
        writeln("fun() called");
        static void r(int a, int b, int c) { writeln("callee called"); }
        return &r;
    }
    int f1() { writeln("f1() called"); return 1; }
    int f2() { writeln("f2() called"); return 2; }
    int f3(int x) { writeln("f3() called"); return x + 3; }
    int f4() { writeln("f4() called"); return 4; }

    // evaluates fun() then f1() then f2() then f3() then f4()
    // after which control is transferred to the callee
    fun()(f1(), f3(f2()), f4());

Implementation Defined: The order of evaluation of function arguments for functions with linkage other than extern(D).

## Lifetime of Temporaries

Expressions and statements may create and/or consume rvalues. Such values are called temporaries and do not have a name or a visible scope. Their lifetime is managed automatically as defined in this section.

For each evaluation that yields a temporary value, the lifetime of that temporary begins at the evaluation point, similarly to creation of a usual named value initialized with an expression.

Termination of lifetime of temporaries does not obey the customary scoping rules and is defined as follows:

- If:
  1. the full expression has a smallest short-circuit expression expr; and
  2. the temporary is created on the right-hand side of the && or || operator; and
  3. the right-hand side is evaluated,
  then temporary destructors are evaluated right after the right-hand side expression has been evaluated and converted to bool. Evaluation of destructors proceeds in reverse order of construction.
- For all other cases, the temporaries generated for the purpose of invoking functions are deferred to the end of the full expression. The order of destruction is inverse to the order of construction.

If a subexpression of an expression throws an exception, all temporaries created up to the evaluation of that subexpression will be destroyed per the rules above. No destructor calls will be issued for temporaries not yet constructed.

Note: An intuition behind these rules is that destructors of temporaries are deferred to the end of full expression and in reverse order of construction, with the exception that the right-hand side of && and || are considered their own full expressions even when part of larger expressions.

Note: The ConditionalExpression e1 ? e2 : e3 is not a special case although it evaluates expressions conditionally: e1 and one of e2 and e3 may create temporaries. Their destructors are inserted to the end of the full expression in the reverse order of creation.

Example:

    import std.stdio;

    struct S
    {
        int x;
        this(int n) { x = n; writefln("S(%s)", x); }
        ~this() { writefln("~S(%s)", x); }
    }

    void main()
    {
        bool b = (S(1) == S(2) || S(3) != S(4)) && S(5) == S(6);
    }

The output of the code above is:

    S(1)
    S(2)
    S(3)
    S(4)
    ~S(4)
    ~S(3)
    S(5)
    S(6)
    ~S(6)
    ~S(5)
    ~S(2)
    ~S(1)

First, S(1) and S(2) are evaluated in lexical order. Per the rules, they will be destroyed at the end of the full expression and in reverse order. The comparison S(1) == S(2) yields false, so the right-hand side of the || is evaluated causing S(3) and S(4) to be evaluated, also in lexical order. However, their destruction is not deferred to the end of the full expression. Instead, S(4) and then S(3) are destroyed at the end of the || expression. Following their destruction, S(5) and S(6) are constructed in lexical order. Again they are not destroyed at the end of the full expression, but right at the end of the && expression. Consequently, the destruction of S(6) and S(5) is carried before that of S(2) and S(1).

## Comma Expression

    CommaExpression:
        AssignExpression
        CommaExpression , AssignExpression

The left operand of the , is evaluated, then the right operand is evaluated. In C, the result of a comma expression is the result of the right operand. In D, using the result of a comma expression isn't allowed. Consequently a comma expression is only useful when each operand has a side effect.

    int x, y;
    // expression statement
    x = 1, y = 1;
    // evaluate a comma expression at the end of each loop iteration
    for (; y < 10; x++, y *= 2)
        writefln("%s, %s", x, y);

Rationale: The comma expression has been used unintentionally, either by bracket nesting mistakes or when users expect a sequence of arguments instead of a single expression. Those bugs can be hard to detect in code review. Disallowing use of the result turns those bugs into errors.

## Assign Expressions

    AssignExpression:
        ConditionalExpression
        ConditionalExpression = AssignExpression
        ConditionalExpression += AssignExpression
        ConditionalExpression -= AssignExpression
        ConditionalExpression *= AssignExpression
        ConditionalExpression /= AssignExpression
        ConditionalExpression %= AssignExpression
        ConditionalExpression &= AssignExpression
        ConditionalExpression |= AssignExpression
        ConditionalExpression ^= AssignExpression
        ConditionalExpression ~= AssignExpression
        ConditionalExpression <<= AssignExpression
        ConditionalExpression >>= AssignExpression
        ConditionalExpression >>>= AssignExpression
        ConditionalExpression ^^= AssignExpression

For all assign expressions, the left operand must be a modifiable lvalue. The type of the assign expression is the type of the left operand, and the result is the value of the left operand after assignment occurs. The resulting expression is a modifiable lvalue.

Undefined Behavior: If either operand is a reference type and one of the following:

1. the operands have partially overlapping storage
2. the operands' storage overlaps exactly but the types are different

Implementation Defined: If neither operand is a reference type and one of the following:

1. the operands have partially overlapping storage
2. the operands' storage overlaps exactly but the types are different

### Simple Assignment Expression

If the operator is = then it is simple assignment.

- If the left operand is a struct that defines opAssign, the behaviour is defined by the overloaded function.
- If the left and right operands are of the same struct type, and the struct type has a Postblit, then the copy operation is as described in Struct Postblit.
- If the lvalue is the .length property of a dynamic array, the behavior is as described in Setting Dynamic Array Length.
- If the lvalue is a static array or a slice, the behavior is as described in Array Copying and Array Setting.
- If the lvalue is a user-defined property, the behavior is as described in Property Functions.

Otherwise, the right operand is implicitly converted to the type of the left operand, and assigned to it.

### Assignment Operator Expressions

For arguments of built-in types, assignment operator expressions such as

    a op= b

are semantically equivalent to:

    a = cast(typeof(a))(a op b)

except that:

- operand a is only evaluated once,
- overloading op uses a different function than overloading op= does, and
- the left operand of >>>= does not undergo Integer Promotions before shifting.

For user-defined types, assignment operator expressions are overloaded separately from the binary operators. Still the left operand must be an lvalue.

## Conditional Expressions

    ConditionalExpression:
        OrOrExpression
        OrOrExpression ? Expression : ConditionalExpression

The first expression is converted to bool, and is evaluated.

If it is true, then the second expression is evaluated, and its result is the result of the conditional expression.

If it is false, then the third expression is evaluated, and its result is the result of the conditional expression.

If either the second or third expressions are of type void, then the resulting type is void. Otherwise, the second and third expressions are implicitly converted to a common type which becomes the result type of the conditional expression.

Note: When a conditional expression is the left operand of an assign expression, parentheses are required for disambiguation:

    bool test;
    int a, b, c;
    ...
    test ? a = b : c = 2;   // error
    (test ? a = b : c) = 2; // OK

This makes the intent clearer, because the first statement can easily be misread as the following code:

    test ? a = b : (c = 2);

## Logical Expressions

See Also:
UnaryExpression for !expr.

### OrOr Expressions

    OrOrExpression:
        AndAndExpression
        OrOrExpression || AndAndExpression

The result type of an OrOrExpression is bool, unless the right operand has type void, when the result is type void.

The OrOrExpression evaluates its left operand.

If the left operand, converted to type bool, evaluates to true, then the right operand is not evaluated. If the result type of the OrOrExpression is bool then the result of the expression is true.

If the left operand is false, then the right operand is evaluated. If the result type of the OrOrExpression is bool then the result of the expression is the right operand converted to type bool.

### AndAnd Expressions

    AndAndExpression:
        OrExpression
        AndAndExpression && OrExpression

The result type of an AndAndExpression is bool, unless the right operand has type void, when the result is type void.

The AndAndExpression evaluates its left operand.

If the left operand, converted to type bool, evaluates to false, then the right operand is not evaluated. If the result type of the AndAndExpression is bool then the result of the expression is false.

If the left operand is true, then the right operand is evaluated. If the result type of the AndAndExpression is bool then the result of the expression is the right operand converted to type bool.

## Bitwise Expressions

Bit wise expressions perform a bitwise operation on their operands. Their operands must be integral types. First, the Usual Arithmetic Conversions are done. Then, the bitwise operation is done.

See Also:
ShiftExpression, ComplementExpression
Note: If an OrExpression, XorExpression or AndExpression appears on either side of an EqualExpression, IdentityExpression or RelExpression, it is a compile error. Instead, disambiguate by using parentheses.

    int x, a, b;
    x = a & 5 == b; // error
    x = a & 5 is b; // error
    x = a & 5 <= b; // error

    x = (a & 5) == b; // OK
    x = a & (5 == b); // OK

### Or Expressions

    OrExpression:
        XorExpression
        OrExpression | XorExpression

The operands are OR'd together.

### Xor Expressions

    XorExpression:
        AndExpression
        XorExpression ^ AndExpression

The operands are XOR'd together.

### And Expressions

    AndExpression:
        CmpExpression
        AndExpression & CmpExpression

The operands are AND'd together.

## Compare Expressions

    CmpExpression:
        EqualExpression
        IdentityExpression
        RelExpression
        InExpression
        ShiftExpression

### Equality Expressions

    EqualExpression:
        ShiftExpression == ShiftExpression
        ShiftExpression != ShiftExpression

Equality expressions compare the two operands for equality (==) or inequality (!=). The type of the result is bool.

Inequality is defined as the logical negation of equality.

- If the operands are integral values, the Usual Arithmetic Conversions are applied to bring them to a common type before comparison. Equality is defined as the bit patterns of the common type match exactly.
- If the operands are pointers, equality is defined as the bit patterns of the operands match exactly. Both types must match, or one can be typeof(null).
- For float, double, and real values, the Usual Arithmetic Conversions are applied to bring them to a common type before comparison. The values -0 and +0 are considered equal. If either or both operands are NaN, then == returns false and != returns true. Otherwise, the bit patterns of the common type are compared for equality.
- For static and dynamic arrays, equality is defined as the lengths of the arrays matching, and the elements in each compare equal. The element types must have a common type.

    assert(5 == 5L);
    assert(byte(4) == 4F);

    int i = 1, j = 1;
    assert(&i != &j);
    assert(&i != null);

    // elements of different types are comparable, even when different sizes
    int[] ia = ['A', 'B', 'C'];
    assert(ia == "ABC");
    byte[] ba = [1, 2];
    assert(ba == [1F, 2F]);

Deprecated:
For complex numbers, equality is defined as equivalent to:

    x.re == y.re && x.im == y.im

#### Class & Struct Equality

For class references, a == b is rewritten to .object.opEquals(a, b), which handles null. This is intended to compare the contents of two objects, however an appropriate opEquals method override must be defined for this to work. The default opEquals provided by the root Object class is equivalent to the is operator.

For struct objects, the expression (a == b) is rewritten as a.opEquals(b), or failing that, b.opEquals(a).

For both class references and struct objects, (a != b) is rewritten as !(a == b).

See opEquals for details.

##### Struct Equality

For struct objects, equality means the result of the opEquals() member function. If an opEquals() is not provided, one will be generated. Equality is defined as the logical product of all equality results of the corresponding object fields.

    struct S
    {
        int i = 4;
        string s = "four";
    }

    S s;
    assert(s == S());
    s.s = "foul";
    assert(s != S());

Implementation Defined: The contents of any alignment gaps in the struct object.

If there are overlapping fields, which happens with unions, the default equality will compare each of the overlapping fields.

Best Practices: An opEquals() can account for which of the overlapping fields contains valid data. An opEquals() can override the default behavior of floating point NaN values always comparing as unequal. Be careful using memcmp() to implement opEquals() if:

- there are any alignment gaps
- any fields have an opEquals()
- there are any floating point fields that may contain NaN or -0 values

### Identity Expressions

    IdentityExpression:
        ShiftExpression is ShiftExpression
        ShiftExpression ! is ShiftExpression

The is operator compares for identity of expression values. To compare for nonidentity, use e1 !is e2. The type of the result is bool. The operands undergo the Usual Arithmetic Conversions to bring them to a common type before comparison.

For class / interface objects, identity is defined as the object references being identical. Class references can be efficiently compared against null using is. Note that interface objects need not have the same reference of the class they were cast from. To test whether an interface shares a class instance with another interface / class value, cast both operands to Object before comparing with is.

    interface I { void g(); }
    interface I1 : I { void g1(); }
    interface I2 : I { void g2(); }
    interface J : I1, I2 { void h(); }

    class C : J
    {
        override void g() { }
        override void g1() { }
        override void g2() { }
        override void h() { }
    }

    void main() @safe
    {
        C c = new C;
        I i1 = cast(I1) c;
        I i2 = cast(I2) c;
        assert(i1 !is i2); // not identical
        assert(c !is i2); // not identical
        assert(cast(Object) i1 is cast(Object) i2); // identical
    }

For struct objects and floating point values, identity is defined as the bits in the operands being identical.

For static and dynamic arrays, identity of two arrays is given when both arrays refer to the same memory location and contain the same number of elements.

    Object o;
    assert(o is null);

    auto a = [1, 2];
    assert(a is a[0..$]);
    assert(a !is a[0..1]);

    auto b = [1, 2];
    assert(a !is b);

Deprecated:
Use of is to compare static arrays by address and length is deprecated. To do so, use the slice operator and compare slices of the arrays instead; for example, a1[] is a2[].

For other operand types, identity is defined as being the same as equality.

The identity operator is cannot be overloaded.

### Relational Expressions

    RelExpression:
        ShiftExpression < ShiftExpression
        ShiftExpression <= ShiftExpression
        ShiftExpression > ShiftExpression
        ShiftExpression >= ShiftExpression

First, the Usual Arithmetic Conversions are done on the operands. The result type of a relational expression is bool.

### Array Comparisons

For static and dynamic arrays, the result of a CmpExpression is the result of the operator applied to the first non-equal element of the array. If two arrays compare equal, but are of different lengths, the shorter array compares as "less" than the longer array.

### Integer Comparisons

Integer comparisons happen when both operands are integral types.

Integer comparison operators
Operator | Relation
--- | ---
< | less
> | greater
<= | less or equal
>= | greater or equal
== | equal
!= | not equal

It is an error to have one operand be signed and the other unsigned for a <, <=, > or >= expression. Use casts to make both operands signed or both operands unsigned.

### Floating Point Comparisons

If one or both operands are floating point, then a floating point comparison is performed.

A CmpExpression can have NaN operands. If either or both operands is NaN, the floating point comparison operation returns as follows:

Floating point comparison operators
Operator | Relation | Returns
--- | --- | ---
< | less | false
> | greater | false
<= | less or equal | false
>= | greater or equal | false
== | equal | false
!= | unordered, less, or greater | true

Best Practices: Although IdentityExpression can be used to check for T.nan, there are other floating-point values for NaN produced at runtime. Use std.math.traits.isNaN to handle all of them.

### Class and Struct Comparisons

For struct objects, a RelExpression performs a comparison which first evaluates a matching opCmp method call.

For class references, a RelExpression performs a comparison which first evaluates to an int which is either:

- 0 if the two object references are identical
- -1 if the left-hand expression is null
- 1 if the right-hand expression is null
- the result of a matching opCmp call

    class C
    {
        override int opCmp(Object o) { assert(0); }
    }

    void main()
    {
        C c;
        //if (c < null) {}  // compile-time error
        assert(c is null);
        assert(c < new C); // C.opCmp is not called
    }

Secondly, for class and struct objects, the evaluated int is compared against zero using the given operator, which forms the result of the RelExpression. For more information, see opCmp.

## In Expressions

    InExpression:
        ShiftExpression in ShiftExpression
        ShiftExpression ! in ShiftExpression

A container such as an associative array can be tested to see if it contains a certain key:

    int[string] foo;
    ...
    if ("hello" in foo)
    {
        // the string was found
    }

The result of an InExpression is a pointer for associative arrays. The pointer is null if the container has no matching key. If there is a match, the pointer points to a value associated with the key.

The !in expression is the logical negation of the in operation.

The in expression has the same precedence as the relational expressions <, <=, etc.

Note: When overloading in, normally only opBinaryRight would be defined. This is because the operation is usually not defined by the key type but by the container, which appears on the right hand side of the in operator.

## Shift Expressions

    ShiftExpression:
        AddExpression
        ShiftExpression << AddExpression
        ShiftExpression >> AddExpression
        ShiftExpression >>> AddExpression

The operands must be integral types, and undergo the Integer Promotions. The result type is the type of the left operand after the promotions. The result value is the result of shifting the bits by the right operand's value.

- << is a left shift.
- >> is a signed right shift.
- >>> is an unsigned right shift.

Implementation Defined: The result of a shift by a negative value or by the same or more bits than the size of the quantity being shifted is undefined. When the shift amount is known at compile time, doing this results in a compile error.

    int c;

    int s = -3;
    auto y = c << s; // implementation defined value

    auto x = c << 33;  // error, max shift count allowed is 31

## Additive Expressions

    AddExpression:
        MulExpression
        AddExpression + MulExpression
        AddExpression - MulExpression
        AddExpression ~ MulExpression

### Add Expressions

In the cases of the Additive operations + and -:

If the operands are of integral types, they undergo the Usual Arithmetic Conversions, and then are brought to a common type using the Usual Arithmetic Conversions.

If both operands are of integral types and an overflow or underflow occurs in the computation, wrapping will happen. For example:

- uint.max + 1 == uint.min
- uint.min - 1 == uint.max
- int.max + 1 == int.min
- int.min - 1 == int.max

If either operand is a floating point type, the other is implicitly converted to floating point and they are brought to a common type via the Usual Arithmetic Conversions.

Add expressions for floating point operands are not associative.

#### Pointer Arithmetic

If the first operand is a pointer, and the second is an integral type, the resulting type is the type of the first operand, and the resulting value is the pointer plus (or minus) the second operand multiplied by the size of the type pointed to by the first operand.

    int[] a = [1,2,3];
    int* p = a.ptr;
    assert(*p == 1);

    *(p + 2) = 4; // same as `p[2] = 4`
    assert(a[2] == 4);

IndexOperation can also be used with a pointer and has the same behaviour as adding an integer, then dereferencing the result.

If the second operand is a pointer, and the first is an integral type, and the operator is +, the operands are reversed and the pointer arithmetic just described is applied.

Producing a pointer through pointer arithmetic is not allowed in @safe code.

If both operands are pointers, and the operator is +, then it is illegal.

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. In this calculation the assumed size of void is one byte. It is an error if the pointers point to different types. The type of the result is ptrdiff_t.

    int[] a = [1,2,3];
    ptrdiff_t d = &a[2] - a.ptr;
    assert(d == 2);

### Cat Expressions

In the case of the Additive operation ~:

A CatExpression concatenates a container's data with other data, producing a new container.

For a dynamic array, the other operand must either be another array or a single value that implicitly converts to the element type of the array. See Array Concatenation.

## Mul Expressions

    MulExpression:
        UnaryExpression
        MulExpression * UnaryExpression
        MulExpression / UnaryExpression
        MulExpression % UnaryExpression

The operands must be arithmetic types. They undergo the Usual Arithmetic Conversions.

For integral operands, the *, /, and % correspond to multiply, divide, and modulus operations. For multiply, overflows are ignored and simply chopped to fit into the integral type.

### Division

For integral operands of the / and % operators, the quotient rounds towards zero and the remainder has the same sign as the dividend.

The following divide or modulus integral operands:

- denominator is 0
- signed int.min is the numerator and -1 is the denominator
- signed long.min is the numerator and -1L is the denominator

are illegal if encountered during Compile Time Execution.

Undefined Behavior: is exhibited if they are encountered during run time. core.checkedint can be used to check for them and select a defined behavior.

### Floating Point

For floating point operands, the * and / operations correspond to the IEEE 754 floating point equivalents. % is not the same as the IEEE 754 remainder. For example, 15.0 % 10.0 == 5.0, whereas for IEEE 754, remainder(15.0,10.0) == -5.0.

Mul expressions for floating point operands are not associative.

## Unary Expressions

    UnaryExpression:
        & UnaryExpression
        ++ UnaryExpression
        -- UnaryExpression
        * UnaryExpression
        - UnaryExpression
        + UnaryExpression
        ! UnaryExpression
        ComplementExpression
        DeleteExpression
        CastExpression
        ThrowExpression
        PowExpression

Operator | Description
--- | ---
& | Take memory address of an lvalue - see pointers
++ | Increment before use - see order of evaluation
-- | Decrement before use
* | Dereference/indirection - typically for pointers
- | Negative
+ | Positive
! | Logical NOT

The usual Integer Promotions are performed prior to unary - and + operations.

### Complement Expressions

    ComplementExpression:
        ~ UnaryExpression

ComplementExpressions work on integral types (except bool). All the bits in the value are complemented. The usual Integer Promotions are performed prior to the complement operation.

### Delete Expressions

    DeleteExpression:
        delete UnaryExpression

Deprecated:
delete has been deprecated. Instead, please use destroy if feasible, or core.memory.__delete as a last resort.

If the UnaryExpression is a class object reference, and there is a destructor for that class, the destructor is called for that object instance.

Next, if the UnaryExpression is a class object reference, or a pointer to a struct instance, and the class or struct has overloaded operator delete, then that operator delete is called for that class object instance or struct instance.

Otherwise, the garbage collector is called to immediately free the memory allocated for the class instance or struct instance.

If the UnaryExpression is a pointer or a dynamic array, the garbage collector is called to immediately release the memory.

The pointer, dynamic array, or reference is set to null after the delete is performed. Any attempt to reference the data after the deletion via another reference to it will result in undefined behavior.

If UnaryExpression is a variable allocated on the stack, the class destructor (if any) is called for that instance. The garbage collector is not called.

Undefined Behavior:

1. Using delete to free memory not allocated by the garbage collector.
2. Referring to data that has been the operand of delete.

### Cast Expressions

    CastExpression:
        cast ( Type ) UnaryExpression
        CastQual

A CastExpression converts the UnaryExpression to Type.

    cast(foo) -p; // cast (-p) to type foo
    (foo) - p;      // subtract p from foo

#### Basic Data Types

For situations where implicit conversions on basic types cannot be performed, the type system may be forced to accept the reinterpretation of a memory region by using a cast.

An example of such a scenario is represented by trying to store a wider type into a narrower one:

    int a;
    byte b = a; // cannot implicitly convert expression a of type int to byte

When casting a source type that is wider than the destination type, the value is truncated to the destination size.

    int a = 64389; // 00000000 00000000 11111011 10000101
    byte b = cast(byte) a;       // 10000101
    ubyte c = cast(ubyte) a;     // 10000101
    short d = cast(short) a;     // 11111011 10000101
    ushort e = cast(ushort) a;   // 11111011 10000101

    writeln(b);
    writeln(c);
    writeln(d);
    writeln(e);

For integral types casting from a narrower type to a wider type is done by performing sign extension.

    ubyte a = 133;  // 10000101
    byte b = a;     // 10000101

    writeln(a);
    writeln(b);

    ushort c = a;   // 00000000 10000101
    short d = b;    // 11111111 10000101

    writeln(c);
    writeln(d);

See also: Casting Integers.

#### Class References

Any casting of a class reference to a derived class reference is done with a runtime check to make sure it really is a downcast. null is the result if it isn't.

    class A {}
    class B : A {}

    void main()
    {
        A a = new A;
        //B b = a;         // error, need cast
        B b = cast(B) a; // b is null if a is not a B
        assert(b is null);

        a = b;         // no cast needed
        a = cast(A) b; // no runtime check needed for upcast
        assert(a is b);
    }

In order to determine if an object o is an instance of a class B use a cast:

    if (cast(B) o)
    {
        // o is an instance of B
    }
    else
    {
        // o is not an instance of B
    }

Casting a pointer type to and from a class type is done as a type paint (i.e. a reinterpret cast).

#### Pointers

Casting a pointer variable to another pointer type modifies the value that will be obtained as a result of dereferencing, along with the number of bytes on which pointer arithmetic is performed.

    int val = 25185; // 00000000 00000000 01100010 01100001
    char *ch = cast(char*)(&val);

    writeln(*ch);    // a
    writeln(cast(int)(*ch)); // 97
    writeln(*(ch + 1));  // b
    writeln(cast(int)(*(ch + 1)));   // 98

Similarly, when casting a dynamically allocated array to a type of smaller size, the bytes of the initial array will be divided and regrouped according to the new dimension.

    import core.stdc.stdlib;

    int *p = cast(int*) malloc(5 * int.sizeof);
    for (int i = 0; i < 5; i++) {
        p[i] = i + 'a';
    }
    // p = [97, 98, 99, 100, 101]

    char* c = cast(char*) p;     // c = [97, 0, 0, 0, 98, 0, 0, 0, 99 ...]
    for (int i = 0; i < 5 * int.sizeof; i++) {
        writeln(c[i]);
    }

When casting a pointer of type A to a pointer of type B and type B is wider than type A, attempts at accessing the memory exceeding the size of A will result in undefined behaviour.

    char c = 'a';
    int *p = cast(int*) (&c);
    writeln(*p);

It is also possible to cast pointers to basic data types. A common practice could be to cast the pointer to an int value and then print its address:

    import core.stdc.stdlib;

    int *p = cast(int*) malloc(int.sizeof);
    int a = cast(int) p;
    writeln(a);

#### Arrays

    T[] a;
    ...
    cast(U[]) a

Casting a non-literal dynamic array a to another dynamic array type U[] is allowed only when the result will contain every byte of data that was referenced by a. This is enforced with a runtime check that the byte length of a's elements is divisible by U.sizeof. If there is a remainder, a runtime error is generated. The cast is done as a type paint, and the resulting array's length is set to (a.length * T.sizeof) / U.sizeof.

    byte[] a = [1,2,3];
    //auto b = cast(int[])a; // runtime error: array cast misalignment

    int[] c = [1, 2, 3];
    auto d = cast(byte[])c; // ok
    // prints:
    // [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]
    writeln(d);

Undefined Behavior: Casting a non-literal array to bool[] when any element has a byte representation other than 0 or 1.

See also: Casting array literals.

A slice of statically known length can be cast to a static array type when the byte counts of their respective data match.

    void f(int[] b)
    {
        char[4] a;
        static assert(!__traits(compiles, a = cast(char[4]) b)); // unknown length
        static assert(!__traits(compiles, a = cast(char[4]) b[0..2])); // too many bytes

        a = cast(char[4]) b[0..1]; // OK
        const i = 1;
        a = cast(char[4]) b[i..2]; // OK
    }

See also: Slice conversion to static array.

#### Static Arrays

Casting a static array to another static array is done only if the array lengths multiplied by the element sizes match; a mismatch is illegal. The cast is done as a type paint (aka a reinterpret cast). The contents of the array are not changed.

    byte[16] b = 3; // set each element to 3
    assert(b[0] == 0x03);
    int[4] ia = cast(int[4]) b;
    // print elements as hex
    foreach (i; ia)
        writefln("%x", i);
    /* prints:
       3030303
       3030303
       3030303
       3030303
     */

#### Integers

Casting an integer to a smaller integral will truncate the value towards the least significant bits. If the target type is signed and the most significant bit is set after truncation, that bit will be lost from the value and the sign bit will be set.

    uint a = 260;
    auto b = cast(ubyte) a;
    assert(b == 4); // truncated like 260 & 0xff

    int c = 128;
    assert(cast(byte)c == -128); // reinterpreted

Converting between signed and unsigned types will reinterpret the value if the destination type cannot represent the source value.

    short c = -1;
    ushort d = c;
    assert(d == ushort.max);
    assert(uint(c) == uint.max);

    ubyte e = 255;
    byte f = e;
    assert(f == -1); // reinterpreted
    assert(short(e) == 255); // no change

#### Floating Point

Casting a floating point literal from one type to another changes its type, but internally it is retained at full precision for the purposes of constant folding.

    void test()
    {
        real a = 3.40483L;
        real b;
        b = 3.40483;     // literal is not truncated to double precision
        assert(a == b);
        assert(a == 3.40483);
        assert(a == 3.40483L);
        assert(a == 3.40483F);
        double d = 3.40483; // truncate literal when assigned to variable
        assert(d != a);     // so it is no longer the same
        const double x = 3.40483; // assignment to const is not
        assert(x == a);     // truncated if the initializer is visible
    }

Casting a floating point value to an integral type is the equivalent of converting to an integer using truncation. If the floating point value is outside the range of the integral type, the cast will produce an invalid result (this is also the case in C, C++).

    void main()
    {
        int a = cast(int) 0.8f;
        assert(a == 0);
        long b = cast(long) 1.5;
        assert(b == 1L);
        long c = cast(long) -1.5;
        assert(c == -1);

        // if the float overflows, the cast returns the integer value of
        // 80000000_00000000H (64-bit operand) or 80000000H (32-bit operand)
        long d = cast(long) float.max;
        assert(d == long.min);
        int e = cast(int) (1234.5 + int.max);
        assert(e == int.min);

        // for types represented on 16 or 8 bits, the result is the same as
        // 32-bit types, but the most significant bits are ignored
        short f = cast(short) float.max;
        assert(f == 0);
    }

#### Structs

An expression e can be cast to a struct type S:

- The compiler attempts a PostfixExpression S(e). If that would fail:
- When e is a struct or static array instance, its data is reinterpreted as the target type S. The data sizes must match.
- Otherwise, it is an error.

Note: The following examples assume a LittleEndian byte order.

    struct S
    {
        int i;
    }
    struct R
    {
        short[2] a;
    }

    S s = cast(S) 5; // same as S(5)
    assert(s.i == 5);
    static assert(!__traits(compiles, cast(S) long.max)); // S(long.max) is invalid

    R r = R([1, 2]);
    s = cast(S) r; // reinterpret r
    assert(s.i == 0x00020001);

    byte[4] a = [1, 0, 2, 0];
    assert(r == cast(R) a); // reinterpret a

A struct instance can be cast to a static array type when their .sizeof properties each give the same result.

    struct S { short a, b, c; }

    S s = S(1, 2, 3);
    static assert(!__traits(compiles, cast(short[2]) s)); // size mismatch

    short[3] x = cast(short[3]) s;
    assert(x.tupleof == s.tupleof);

    auto y = cast(byte[6]) s;
    assert(y == [1, 0, 2, 0, 3, 0]);

#### Qualifier Cast

    CastQual:
        cast ( TypeCtorsopt ) UnaryExpression

A CastQual replaces the qualifiers in the type of the UnaryExpression:

    shared int x;
    static assert(is(typeof(cast(const)x) == const int));

Casting with no type or qualifiers removes any top level const, immutable, shared or inout type modifiers from the type of the UnaryExpression.

    shared int x;
    static assert(is(typeof(cast()x) == int));

#### Casting to void

Casting an expression to void type is allowed to mark that the result is unused. On ExpressionStatement, it could be used properly to avoid a "has no effect" error.

    void foo(lazy void exp) {}
    void main()
    {
        foo(10);            // NG - expression '10' has no effect
        foo(cast(void)10);  // OK
    }

## Throw Expression

    ThrowExpression:
        throw AssignExpression

AssignExpression is evaluated and must yield a reference to a Throwable or a class derived from Throwable. The reference is thrown as an exception, interrupting the current control flow to continue at a suitable catch clause of a try-statement. This process will execute any applicable scope (exit) / scope (failure) passed since entering the corresponding try block.

    throw new Exception("message");

The Throwable must not be a qualified as immutable, const, inout or shared. The runtime may modify a thrown object (e.g. to contain a stack trace) which would violate const or immutable objects.

A ThrowExpression may be nested in another expression:

    void foo(int function() f) {}

    void main() {
        foo(() => throw new Exception());
    }

The type of a ThrowExpression is noreturn.

Best Practices: Use Assert Expressions rather than Error to report program bugs and abort the program.

## Pow Expressions

    PowExpression:
        PostfixExpression
        PostfixExpression ^^ UnaryExpression

PowExpression raises its left operand to the power of its right operand.

## Postfix Expressions

    PostfixExpression:
        PrimaryExpression
        PostfixExpression . Identifier
        PostfixExpression . TemplateInstance
        PostfixExpression . NewExpression
        PostfixExpression ++
        PostfixExpression --
        PostfixExpression ( NamedArgumentListopt )
        TypeCtorsopt BasicType ( NamedArgumentListopt )
        PostfixExpression IndexOperation
        PostfixExpression SliceOperation

Operation | Description
--- | ---
. Identifier | Either: Access a property of a type or expression. Access a member of a module, package, aggregate type or instance, enum or template instance. Dereference a pointer to a struct/union instance and access a member of it. Call a free function using UFCS.
. NewExpression | Instantiate a nested class
++ | Increment after use - see order of evaluation
-- | Decrement after use
(args) | Either: Call an expression with optional arguments Call static opCall on a user-defined type Construct a type with optional arguments
IndexOperation | Select a single element
SliceOperation | Select a series of elements

### Postfix Argument Lists

    ArgumentList:
        AssignExpression
        AssignExpression ,
        AssignExpression , ArgumentList

    NamedArgumentList:
        NamedArgument
        NamedArgument ,
        NamedArgument , NamedArgumentList

    NamedArgument:
        Identifier : AssignExpression
        AssignExpression

#### Callable Expressions

A callable expression can precede a list of named arguments in parentheses. The following expressions can be called:

- A function
- A function pointer
- A delegate
- An aggregate type instance which defines opCall

    void f(int, int);

    void g()
    {
        f(5, 6);
        (&f)(5, 6);
    }

#### Matching Arguments to Parameters

Arguments in a NamedArgumentList are matched to function parameters as follows:

1. If the first argument has no name, it will be assigned to the first function parameter.
2. A named argument is assigned to a function parameter with the same name. It is an error if no such parameter exists.
3. Any unnamed argument is assigned to the next parameter relative to the preceding argument's parameter. It is an error if no such parameter exists, i.e. when the preceding argument assigns to the last parameter.
4. Assigning a parameter more than once is an error.
5. Not assigning a parameter an argument is also an error, unless the parameter has a Default Argument.

#### Constructing a Type with an Argument List

A type can precede a list of arguments. See:

- Struct Literals
- Struct Constructors
- Uniform construction syntax for built-in scalar types

### Index Operations

    IndexOperation:
        [ ArgumentList ]

The base PostfixExpression is evaluated. The special variable $ is declared and set to be the number of elements in the base PostfixExpression (when available). A new declaration scope is created for the evaluation of the ArgumentList and $ appears in that scope only.

If the PostfixExpression is an expression of static or dynamic array type, the result of the indexing is an lvalue of the ith element in the array, where i is an integer evaluated from ArgumentList. If PostfixExpression is a pointer p, the result is *(p + i) (see Pointer Arithmetic).

If the base PostfixExpression is a ValueSeq then the ArgumentList must consist of only one argument, and that must be statically evaluatable to an integral constant. That integral constant n then selects the nth expression in the ValueSeq, which is the result of the IndexOperation. It is an error if n is out of bounds of the ValueSeq.

The index operator can be overloaded. Using multiple indices in ArgumentList is only supported for operator overloading.

### Slice Operations

    SliceOperation:
        [ ]
        [ Slice ]
        [ Slice , ]

    Slice:
        AssignExpression
        AssignExpression , Slice
        AssignExpression .. AssignExpression
        AssignExpression .. AssignExpression , Slice

The base PostfixExpression is evaluated. The special variable $ is declared and set to be the number of elements in the PostfixExpression (when available). A new declaration scope is created for the evaluation of the AssignExpression..AssignExpression and $ appears in that scope only.

If the base PostfixExpression is a static or dynamic array a, the result of the slice is a dynamic array referencing elements a[i] to a[j-1] inclusive, where i and j are integers evaluated from the first and second AssignExpression respectively.

If the base PostfixExpression is a pointer p, the result will be a dynamic array referencing elements from p[i] to p[j-1] inclusive, where i and j are integers evaluated from the first and second AssignExpression respectively.

If the base PostfixExpression is a ValueSeq, then the result of the slice is a new ValueSeq formed from the upper and lower bounds, which must statically evaluate to integral constants. It is an error if those bounds are out of range.

The first AssignExpression is taken to be the inclusive lower bound of the slice, and the second AssignExpression is the exclusive upper bound. The result of the expression is a slice of the elements in PostfixExpression.

If the [ ] form is used, the slice is of all the elements in the base PostfixExpression. The base expression cannot be a pointer.

The slice operator can be overloaded. Using more than one Slice is only supported for operator overloading.

A SliceOperation is not a modifiable lvalue.

#### Slice Conversion to Static Array

If the slice bounds can be known at compile time, the slice expression may be implicitly convertible to a static array lvalue. For example:

    arr[a .. b]     // typed T[]

If both a and b are integers (which may be constant-folded), the slice expression can be converted to a static array of type T[b - a].

Note: a static array can also be assigned from a slice, performing a runtime check that the lengths match.

    void f(int[2] sa) {}

    int[] arr = [1, 2, 3];

    void test()
    {
        //f(arr); // error, can't convert
        f(arr[1 .. 3]); // OK
        //f(arr[0 .. 3]); // error

        int[2] g() { return arr[0 .. 2]; }
    }

    void bar(ref int[2] a)
    {
        assert(a == [2, 3]);
        a = [4, 5];
    }

    void main()
    {
        int[] arr = [1, 2, 3];

        // slicing an lvalue gives an lvalue
        bar(arr[1 .. 3]);
        assert(arr == [1, 4, 5]);
    }

## Primary Expressions

    PrimaryExpression:
        Identifier
        . Identifier
        TemplateInstance
        . TemplateInstance
        $
        LiteralExpression
        AssertExpression
        MixinExpression
        ImportExpression
        NewExpression
        FundamentalType . Identifier
        TypeCtoropt ( Type ) . Identifier
        ( Type ) . TemplateInstance
        FundamentalType ( NamedArgumentListopt )
        TypeCtoropt ( Type ) ( NamedArgumentListopt )
        Typeof
        TypeidExpression
        IsExpression
        ( Expression )
        SpecialKeyword
        RvalueExpression
        TraitsExpression

    LiteralExpression:
        this
        super
        null
        true
        false
        IntegerLiteral
        FloatLiteral
        CharacterLiteral
        StringLiteral
        InterpolationExpressionSequence
        ArrayLiteral
        AssocArrayLiteral
        FunctionLiteral

Expression | Description
--- | ---
. Identifier | Module Scope Operator
$ | Number of elements in an object being indexed/sliced.
( Type ). Identifier | Access a type property or a static member of a type.
FundamentalType (arg) | Uniform construction of scalar type with optional argument.
( Type )(args) | Construct a type with optional arguments.
( Expression ) | Evaluate an expression - useful as a subexpression.

### this

Within a constructor or non-static member function, this resolves to a reference to the object for which the function was called.

typeof(this) is valid anywhere inside an aggregate type definition. If a class member function is called with an explicit reference to typeof(this), a non-virtual call is made:

    class A
    {
        char get() { return 'A'; }

        char foo() { return typeof(this).get(); } // calls `A.get`
        char bar() { return this.get(); } // dynamic, same as just `get()`
    }

    class B : A
    {
        override char get() { return 'B'; }
    }

    void main()
    {
        B b = new B();

        assert(b.foo() == 'A');
        assert(b.bar() == 'B');
    }

Assignment to this is not allowed for classes.

See also:

- Delegating Constructors
- template this parameters

### super

super is identical to this, except that it is cast to this's base class. It is an error if there is no base class. (The only extern(D) class without a base class is Object, however, note that extern(C++) classes have no base class unless specified.) If a member function is called with an explicit reference to super, a non-virtual call is made.

Assignment to super is not allowed.

See also: Base Class Construction.

### null

null represents the null value for pointers, pointers to functions, delegates, dynamic arrays, associative arrays, and class objects. If it has not already been cast to a type, it is given the singular type typeof(null) and it is an exact conversion to convert it to the null value for pointers, pointers to functions, delegates, etc. After it is cast to a type, such conversions are implicit, but no longer exact.

### String Literals

See StringLiteral grammar.

String literals are read-only. A string literal without a StringPostfix can implicitly convert to any of the following types, which have equal weight:

immutable(char)*
immutable(wchar)*
immutable(dchar)*
immutable(char)[]
immutable(wchar)[]
immutable(dchar)[]

Undefined Behavior: writing to a string literal. This is not allowed in @safe code.

By default, a string literal is typed as a dynamic array, but the element count is known at compile time. So all string literals can be implicitly converted to an immutable static array:

    void foo(char[2] a)
    {
        assert(a[0] == 'b');
    }
    void bar(ref const char[2] a)
    {
        assert(a == "bc");
    }

    void main()
    {
        foo("bc");
        foo("b"); // OK
        //foo("bcd"); // error, too many chars
        bar("bc"); // OK, same length
        //bar("b"); // error, lengths must match
    }

A string literal converts to a static array rvalue of the same or longer length. Any extra elements are padded with zeros. A string literal can also convert to a static array lvalue of the same length.

String literals have a '\0' appended to them, which makes them easy to pass to C or C++ functions expecting a null-terminated const char* string. The '\0' is not included in the .length property of the string literal.

Concatenation of string literals requires the use of the ~ operator, and is resolved at compile time. C style implicit concatenation without an intervening operator is error prone and not supported in D.

### Hex String Literals

Because hex string literals contain binary data not limited to textual data, they allow additional conversions over other string literals.

A hex string literal implicitly converts to a constant byte[] or ubyte[].

    immutable ubyte[] b = x"3F 80 00 00";
    const byte[] c = x"3F 80 00 00";

A hex string literal can be explicitly cast to an array of integers with a larger size than 1. A big endian byte order in the hex string will be assumed.

    static immutable uint[] data = cast(immutable uint[]) x"AABBCCDD";
    static assert(data[0] == 0xAABBCCDD);

This requires the length of the hex string to be a multiple of the array element's size in bytes.

    static e = cast(immutable ushort[]) x"AA BB CC";
    // Error, length of 3 bytes is not a multiple of 2, the size of a `ushort`

When a hex string literal gets constant folded, the result is no longer considered a hex string literal

    static immutable byte[] b = x"AA" ~ "G"; // Error: cannot convert `string` to `immutable byte[]`

### Array Literals

    ArrayLiteral:
        [ ArgumentListopt ]

An array literal is a comma-separated list of expressions between square brackets [ and ]. The expressions form the elements of a dynamic array. The length of the array is the number of elements.

The element type of the array is inferred as the common type of all the elements, and each expression is implicitly converted to that type. When there is an expected array type, the elements of the literal will be implicitly converted to the expected element type.

    auto a1 = [1, 2, 3];   // type is int[], with elements 1, 2 and 3
    auto a2 = [1u, 2, 3];  // type is uint[], with elements 1u, 2u, and 3u
    byte[] a3 = [1, 2, 3]; // OK
    byte[] a4 = [128];     // error

By default, an array literal is typed as a dynamic array, but the element count is known at compile time. Therefore, an array literal can be implicitly converted to a static array of the same length.

    int[2] sa = [1, 2]; // OK
    int[2] sb = [1];    // error

Note: Slicing a dynamic array with a statically known slice length also allows conversion to a static array.

If any ArrayMemberInitialization is a ValueSeq, then the elements of the ValueSeq are inserted as expressions in place of the sequence.

#### GC Allocation

Escaping array literals are always allocated on the memory managed heap. Thus, they can be returned safely from functions:

    int[] foo()
    {
        return [1, 2, 3];
    }

An array literal is not GC allocated if:

- It initializes or assigns to a static array.
- It is an argument to a scope function parameter.
- It initializes a scope slice (-preview=dip1000 is required for this).
- It is used on one side of an EqualExpression or RelExpression.
- It is immediately indexed and used as an rvalue.
- It is used as a foreach aggregate where the element variable is not ref.

    void f(scope int[] a, int[2] sa) @nogc
    {
        sa = [7, 8];
    }
    void g(int[] b) @nogc; // `b` is not scope, so may escape

    void main() @nogc
    {
        int[3] sa = [1, 2, 3];
        f([1, 2], [3, 4]);
        //scope int[] a = [5, 6]; // requires `-preview=dip1000`
        //g([1, 2]); // error, array literal heap allocated
        assert([1, 2] < [3, 2]);
        assert([1, 2][1] == 2);
        foreach (e; [4, 2, 9])
            assert(e > 0);
    }

#### Casting

When array literals are cast to another array type, each element of the array is cast to the new element type. When arrays that are not literals are cast, the array is reinterpreted as the new type, and the length is recomputed:

    // cast array literal
    const ubyte[] ct = cast(ubyte[]) [257, 257];
    // this is equivalent to:
    // const ubyte[] ct = [cast(ubyte) 257, cast(ubyte) 257];
    writeln(ct);  // writes [1, 1]

    // cast other array expression
    // --> normal behavior of CastExpression
    byte[] arr = [1, 1];
    short[] rt = cast(short[]) arr;
    writeln(rt);  // writes [257]

In other words, casting an array literal will change the type of each initializer element.
Best Practices: Avoid casting an array literal when the elements could implicitly convert to an expected type. Instead, declare a variable of that type and initialize it with the array literal. Casting is more bug-prone than implicit conversions.

### Associative Array Literals

    AssocArrayLiteral:
        [ KeyValuePairs ]

    KeyValuePairs:
        KeyValuePair
        KeyValuePair , KeyValuePairs

    KeyValuePair:
        KeyExpression : ValueExpression

    KeyExpression:
        AssignExpression

    ValueExpression:
        AssignExpression

Associative array literals are a comma-separated list of key: value pairs between square brackets [ and ]. The list cannot be empty. The common type of the all keys is taken to be the key type of the associative array, and all keys are implicitly converted to that type. The common type of the all values is taken to be the value type of the associative array, and all values are implicitly converted to that type. An AssocArrayLiteral cannot be used to statically initialize anything.

    [21u: "he", 38: "ho", 2: "hi"]; // type is string[uint],
                                  // with keys 21u, 38u and 2u
                                  // and values "he", "ho", and "hi"

If any of the keys or values in the KeyValuePairs are a ValueSeq, then the elements of the ValueSeq are inserted as arguments in place of the sequence.

Associative array initializers may contain duplicate keys, however, in that case, the last KeyValuePair lexicographically encountered is stored.

    auto aa = [21: "he", 38: "ho", 2: "hi", 2:"bye"];
    assert(aa[2] == "bye")

### Function Literals

    FunctionLiteral:
        function RefOrAutoRefopt BasicTypeWithSuffixesopt ParameterWithAttributesopt FunctionLiteralBody
        delegate RefOrAutoRefopt BasicTypeWithSuffixesopt ParameterWithMemberAttributesopt FunctionLiteralBody
        RefOrAutoRefopt ParameterWithMemberAttributes FunctionLiteralBody
        BlockStatement
        Identifier => AssignExpression

    BasicTypeWithSuffixes:
        BasicType TypeSuffixesopt

    ParameterWithAttributes:
        Parameters FunctionAttributesopt

    ParameterWithMemberAttributes:
        Parameters MemberFunctionAttributesopt

    FunctionLiteralBody:
        => AssignExpression
        SpecifiedFunctionBody

    RefOrAutoRef:
        ref
        auto ref

FunctionLiterals enable embedding anonymous functions and anonymous delegates directly into expressions. Short function literals are known as lambdas.

- Type is the return type of the function or delegate - if omitted it is inferred.
- ParameterWithAttributes or ParameterWithMemberAttributes can be used to specify the parameters for the function. If these are omitted, the function defaults to the empty parameter list ( ).
- Parameter types can be omitted.
- The type of a function literal is a delegate or a pointer to function.

For example:

    int function(char c) fp; // declare pointer to a function

    void test()
    {
        static int foo(char c) { return 6; }

        fp = &foo;
    }

is exactly equivalent to:

    int function(char c) fp;

    void test()
    {
        fp = function int(char c) { return 6; };
    }

A delegate is necessary if the FunctionLiteralBody accesses any non-static local variables in enclosing functions.

    int abc(int delegate(int i));

    void test()
    {
        int b = 3;
        int foo(int c) { return 6 + b; }

        abc(&foo);
    }

is exactly equivalent to:

    int abc(int delegate(int i));

    void test()
    {
        int b = 3;

        abc( delegate int(int c) { return 6 + b; } );
    }

The use of ref declares that the return value is returned by reference:

    void main()
    {
        int x;
        auto dg = delegate ref int() { return x; };
        dg() = 3;
        assert(x == 3);
    }

Note: When comparing function literals with nested functions, the function form is analogous to static or non-nested functions, and the delegate form is analogous to non-static nested functions. I.e. a delegate literal can access non-static local variables in an enclosing function, a function literal cannot.

#### Delegate Inference

If a literal omits function or delegate and there's no expected type from the context, then it is inferred to be a delegate if it accesses a variable in an enclosing function, otherwise it is a function pointer.

    void test()
    {
        int b = 3;

        auto fp = (uint c) { return c * 2; }; // inferred as function pointer
        auto dg = (int c) { return 6 + b; }; // inferred as delegate

        static assert(!is(typeof(fp) == delegate));
        static assert(is(typeof(dg) == delegate));
    }

If a delegate is expected, the literal will be inferred as a delegate even if it accesses no variables from an enclosing function:

    void abc(int delegate(int i)) {}
    void def(uint function(uint s)) {}

    void test()
    {
        int b = 3;

        abc( (int c) { return 6 + b; } );  // inferred as delegate
        abc( (int c) { return c * 2; } );  // inferred as delegate

        def( (uint c) { return c * 2; } ); // inferred as function
        //def( (uint c) { return c * b; } );  // error!
        // Because the FunctionLiteral accesses b, its type
        // is inferred as delegate. But def cannot accept a delegate argument.
    }

#### Parameter Type Inference

If the type of a function literal can be uniquely determined from its context, parameter type inference is possible.

    void foo(int function(int) fp);

    void test()
    {
        int function(int) fp = (n) { return n * 2; };
        // The type of parameter n is inferred as int.

        foo((n) { return n * 2; });
        // The type of parameter n is inferred as int.
    }

    auto fp = (i) { return 1; }; // error, cannot infer type of `i`

#### Function Literal Aliasing

Function literals can be aliased. Aliasing a function literal with unspecified parameter types produces a function template with type parameters for each unspecified parameter type of the literal. Type inference for the literal is then done when the template is instantiated.

    alias fpt = (i) { return i; }; // ok, infer type of `i` when used
    //auto fpt(T)(T i) { return i; } // equivalent

    auto v = fpt(4);    // `i` is inferred as int
    auto d = fpt(10.3); // `i` is inferred as double

    alias fp = fpt!float;
    auto f = fp(0); // f is a float

#### Return Type Inference

The return type of the FunctionLiteral can be inferred from either the AssignExpression, or any ReturnStatements in the BlockStatement. If there is a different expected type from the context, and the initial inferred return type implicitly converts to the expected type, then the return type is inferred as the expected type.

    auto fi = (int i) { return i; };
    static assert(is(typeof(fi(5)) == int));

    long function(int) fl = (int i) { return i; };
    static assert(is(typeof(fl(5)) == long));

#### Nullary Short Syntax

Parameters can be omitted completely for a function literal when there is a BlockStatement function body.

Note: This form is not allowed to be immediately called as an ExpressionStatement, because it would require arbitrary lookahead to distinguish it from a BlockStatement.

    auto f = { writeln("hi"); }; // OK, f has type `void function()`
    f();
    { writeln("hi"); }(); // error
    () { writeln("hi"); }(); // OK

Anonymous delegates can behave like arbitrary statement literals. For example, here an arbitrary statement is executed by a loop:

    void loop(int n, void delegate() statement)
    {
        foreach (_; 0 .. n)
        {
            statement();
        }
    }

    void main()
    {
        int n = 0;

        loop(5, { n += 1; });
        assert(n == 5);
    }

#### Shortened Body Syntax

The syntax => AssignExpression is equivalent to { return AssignExpression; }.

    void main()
    {
        auto i = 3;
        auto twice = function (int x) => x * 2;
        assert(twice(i) == 6);

        auto square = delegate () => i * i;
        assert(square() == 9);

        auto n = 5;
        auto mul_n = (int x) => x * n;
        assert(mul_n(i) == 15);
    }

The syntax Identifier => AssignExpression is equivalent to (Identifier) { return AssignExpression; }.

    // the following two declarations are equivalent
    alias fp = i => 1;
    alias fp = (i) { return 1; };

Best Practices: The minimal form of the function literal is most useful as an argument to a template alias parameter:

    int motor(alias fp)(int i)
    {
        return fp(i) + 1;
    }

    int engine()
    {
        return motor!(i => i * 2)(6); // returns 13
    }

Note: The syntax Identifier { statement; } is not supported because it is easily confused with statements x = Identifier; { statement; }; if the semicolons were accidentally omitted.

### Uniform construction syntax for built-in scalar types

The implicit conversions of built-in scalar types can be explicitly represented by using function call syntax. For example:

    auto a = short(1);  // implicitly convert an integer literal '1' to short
    auto b = double(a); // implicitly convert a short variable 'a' to double
    auto c = byte(128); // error, 128 cannot be represented in a byte

If the argument is omitted, it means default construction of the scalar type:

    auto a = ushort();  // same as: ushort.init
    auto b = wchar();   // same as: wchar.init

The argument may not be given a name:

    auto a = short(x: 1); // Error

See also: Usual Arithmetic Conversions.

### Assert Expressions

    AssertExpression:
        assert ( AssertArguments )

    AssertArguments:
        AssignExpression
        AssignExpression ,
        AssignExpression , AssignExpression
        AssignExpression , AssignExpression ,

The first AssignExpression is evaluated and converted to a boolean value. If the value is not true, an Assert Failure has occurred and the program enters an Invalid State.

    int i = fun();
    assert(i > 0);

AssertExpression has different semantics if it is in a unittest or in contract.

If the first AssignExpression is a reference to a class instance for which a class Invariant exists, the class Invariant must hold.

If the first AssignExpression is a pointer to a struct instance for which a struct Invariant exists, the struct Invariant must hold.

The type of an AssertExpression is void.

Undefined Behavior: Once in an Invalid State the behavior of the continuing execution of the program is undefined.
Implementation Defined: Whether the first AssertExpression is evaluated or not (at runtime) is typically set with a compiler switch. If it is not evaluated, any side effects specified by the AssertExpression may not occur. The behavior when the first AssertExpression evaluates to false is also typically set with a compiler switch, and may include these options:

1. Immediately halting via execution of a special CPU instruction
2. Aborting the program
3. Calling the assert failure function in the corresponding C runtime library
4. Throwing the AssertError exception in the D runtime library

Note: Throwing AssertError is the default for dmd, with an optional -checkaction=context switch to show certain sub-expressions used in the first AssertExpression in the error message:

    auto x = 4;
    assert(x < 3);

When in use, the above will throw an AssertError with a message 4 >= 3.
Best Practices:

1. Do not have side effects in either AssignExpression that subsequent code depends on.
2. AssertExpressions are intended to detect bugs in the program. Do not use them for detecting input or environmental errors.
3. Do not attempt to resume normal execution after an Assert Failure.

#### Compile-time Evaluation

If the first AssignExpression consists entirely of compile time constants, and evaluates to false, it is a special case - it signifies that subsequent statements are unreachable code. Compile Time Function Execution (CTFE) is not attempted.

This allows the compiler to suppress an error when there is a missing return statement:

    int f(int x)
    {
        if (x > 0)
        {
            return 5 / x;
        }
        assert(0);
        // no need to use a dummy return statement here
    }

The implementation may handle the case of the first AssignExpression evaluating to false at compile time differently - even when other asserts are ignored, it may still generate a HLT instruction or equivalent.

Rationale: Halting the program prevents undefined behaviour from occurring.

See also: static assert.

#### Assert Message

The second AssignExpression, if present, must be implicitly convertible to type const(char)[]. When present, the implementation may evaluate it and print the resulting message upon assert failure:

    void main()
    {
        assert(0, "an" ~ " error message");
    }

When compiled and run, typically it will produce the message:

    core.exception.AssertError@test.d(3) an error message

### Mixin Expressions

    MixinExpression:
        mixin ( ArgumentList )

Each AssignExpression in the ArgumentList is evaluated at compile time, and the result must be representable as a string. The resulting strings are concatenated to form a string. The text contents of the string must be compilable as a valid Expression, and is compiled as such.

    int foo(int x)
    {
        return mixin("x +", 1) * 7;  // same as ((x + 1) * 7)
    }

### Import Expressions

    ImportExpression:
        import ( AssignExpression )

The AssignExpression must evaluate at compile time to a constant string. The text contents of the string are interpreted as a file name. The file is read, and the exact contents of the file become a hex string literal.

Implementations may restrict the file name in order to avoid directory traversal security vulnerabilities. A possible restriction might be to disallow any path components in the file name.

Note that by default an import expression will not compile unless one or more paths are passed via the -J switch. This tells the compiler where it should look for the files to import. This is a security feature.

    void foo()
    {
        // Prints contents of file foo.txt
        writeln(import("foo.txt"));
    }

### New Expressions

    NewExpression:
        new Type
        new Type [ AssignExpression ]
        new Type ( NamedArgumentListopt )
        NewAnonClassExpression

NewExpressions allocate memory on the garbage collected heap by default.

new T constructs an instance of type T and default-initializes it. The result's type is:

- T when T is a reference type (e.g. classes, associative arrays)
- T* when T is a value type (e.g. basic types, structs)

    int* i = new int;
    assert(*i == 0); // int.init

    Object o = new Object;
    //int[] a = new int[]; // error, need length argument

The `Type(NamedArgumentList)` form allows passing either a single initializer of the same type, or multiple arguments for more complex types:

- For class and struct types, NamedArgumentList is passed to the constructor.
- For a dynamic array, the argument sets the initial array length.
- For multidimensional dynamic arrays, each argument corresponds to an initial length (see below).

    int* i = new int(5);
    assert(*i == 5);

    Exception e = new Exception("info");
    assert(e.msg == "info");

    int[] a = new int[](2);
    assert(a.length == 2);
    a = new int[2]; // same, see below

The `Type[AssignExpression]` form allocates a dynamic array with length equal to AssignExpression. It is preferred to use the `Type(NamedArgumentList)` form when allocating dynamic arrays instead, as it is more general.

Note: It is not possible to allocate a static array directly with new (only by using a type alias).

The result is a unique expression which can implicitly convert to other qualifiers:

    immutable o = new Object;

#### Class Instantiation

If a NewExpression is used with a class type as an initializer for a function local variable with scope storage class, then the instance is allocated on the stack.

new can also be used to allocate a nested class.

#### Multidimensional Arrays

To allocate multidimensional arrays, the declaration reads in the same order as the prefix array declaration order.

    char[][] foo;   // dynamic array of strings
    ...
    foo = new char[][30]; // allocate array of 30 strings

The above allocation can also be written as:

    foo = new char[][](30); // allocate array of 30 strings

To allocate the nested arrays, multiple arguments can be used:

    int[][][] bar;
    bar = new int[][][](5, 20, 30);

    assert(bar.length == 5);
    assert(bar[0].length == 20);
    assert(bar[0][0].length == 30);

The assignment above is equivalent to:

    bar = new int[][][5];
    foreach (ref a; bar)
    {
        a = new int[][20];
        foreach (ref b; a)
        {
            b = new int[30];
        }
    }

### Typeid Expressions

    TypeidExpression:
        typeid ( Type )
        typeid ( Expression )

If Type, returns an instance of class TypeInfo corresponding to Type.

If Expression, returns an instance of class TypeInfo corresponding to the type of the Expression. If the type is a class, it returns the TypeInfo of the dynamic type (i.e. the most derived type). The Expression is always executed.

    class A { }
    class B : A { }

    void main()
    {
        import std.stdio;

        writeln(typeid(int));        // int
        uint i;
        writeln(typeid(i++));        // uint
        writeln(i);                  // 1
        A a = new B();
        writeln(typeid(a));          // B
        writeln(typeid(typeof(a)));  // A
    }

### Is Expressions

    IsExpression:
        is ( Type )
        is ( Type : TypeSpecialization )
        is ( Type == TypeSpecialization )
        is ( Type : TypeSpecialization , TemplateParameterList )
        is ( Type == TypeSpecialization , TemplateParameterList )
        is ( Type Identifier )
        is ( Type Identifier : TypeSpecialization )
        is ( Type Identifier == TypeSpecialization )
        is ( Type Identifier : TypeSpecialization , TemplateParameterList )
        is ( Type Identifier == TypeSpecialization , TemplateParameterList )

    TypeSpecialization:
        Type
        TypeCtor
        struct
        union
        class
        interface
        enum
        __vector
        function
        delegate
        super
        return
        __parameters
        module
        package

An IsExpression is evaluated at compile time and is used to check if an expression is a valid type. In addition, there are forms which can also:

- compare types for equivalence
- determine if one type can be implicitly converted to another
- deduce the subtypes of a type using pattern matching
- deduce the template arguments of a type template instance

The result of an IsExpression is a boolean which is true if the condition is satisfied and false if not.

Type is the type being tested. It must be syntactically correct, but it need not be semantically correct. If it is not semantically correct, the condition is not satisfied.

TypeSpecialization is the type that Type is being pattern matched against.

IsExpressions may be used in conjunction with typeof to check whether an expression type checks correctly. For example, is(typeof(foo)) will return true if foo has a valid type.

#### Basic Forms

##### is ( Type )

The condition is satisfied if Type is semantically correct. Type must be syntactically correct regardless.

    pragma(msg, is(5)); // error
    pragma(msg, is([][])); // error

    int i;
    static assert(is(int));
    static assert(is(typeof(i))); // same

    static assert(!is(Undefined));
    static assert(!is(typeof(int))); // int is not an expression
    static assert(!is(i)); // i is a value

    alias Func = int(int); // function type
    static assert(is(Func));
    static assert(!is(Func[])); // fails as an array of functions is not allowed

##### is ( Type : TypeSpecialization )

The condition is satisfied if Type is semantically correct and it is the same as or can be implicitly converted to TypeSpecialization. TypeSpecialization is only allowed to be a Type.

    alias Bar = short;
    static assert(is(Bar : int)); // short implicitly converts to int
    static assert(!is(Bar : string));

##### is ( Type == TypeSpecialization )

If TypeSpecialization is a type, the condition is satisfied if Type is semantically correct and is the same type as TypeSpecialization.

    alias Bar = short;
    static assert(is(Bar == short));
    static assert(!is(Bar == int));

If TypeSpecialization is a TypeCtor then the condition is satisfied if Type is of that TypeCtor:

    static assert(is(const int == const));
    static assert(is(const int[] == const));
    static assert(!is(const(int)[] == const)); // head is mutable
    static assert(!is(immutable int == const));

If TypeSpecialization is one of struct union class interface enum __vector function delegate module package then the condition is satisfied if Type is one of those.

    static assert(is(Object == class));
    static assert(is(ModuleInfo == struct));
    static assert(!is(int == class));

The module and package forms are satisfied when Type is a symbol, not a type, unlike the other forms. The isModule and isPackage __traits should be used instead. Package modules are considered to be both packages and modules.

TypeSpecialization can also be one of these keywords:

keyword | condition
--- | ---
super | true if Type is a class or interface
return | true if Type is a function, delegate or function pointer
__parameters | true if Type is a function, delegate or function pointer

    class C {}
    static assert(is(C == super));

    void foo(int i);
    static assert(!is(foo == return));
    static assert(is(typeof(foo) == return));
    static assert(is(typeof(foo) == __parameters));

See also: Traits.

#### Identifier Forms

Identifier is declared to be an alias of the resulting type if the condition is satisfied. The Identifier forms can only be used if the IsExpression appears in a StaticIfCondition or the first argument of a StaticAssert.

##### is ( Type Identifier )

The condition is satisfied if Type is semantically correct. If so, Identifier is declared to be an alias of Type.

    struct S
    {
        int i, j;
    }
    static assert(is(typeof(S.i) T) && T.sizeof == 4);

    alias Bar = short;

    void foo()
    {
        static if (is(Bar T))
            alias S = T;
        else
            alias S = long;

        pragma(msg, S); // short

        // if T was defined, it remains in scope
        if (is(T))
            pragma(msg, T); // short

        //if (is(Bar U)) {} // error, cannot declare U here
    }

##### is ( Type Identifier : TypeSpecialization )

If TypeSpecialization is a type, the condition is satisfied if Type is semantically correct and it is the same as or can be implicitly converted to TypeSpecialization. Identifier is declared to be an alias of the TypeSpecialization.

    alias Bar = int;

    static if (is(Bar T : int))
        alias S = T;
    else
        alias S = long;

    static assert(is(S == int));

If TypeSpecialization is a type pattern involving Identifier, type deduction of Identifier is attempted based on either Type or a type that it implicitly converts to. The condition is only satisfied if the type pattern is matched.

    struct S
    {
        long* i;
        alias i this; // S converts to long*
    }

    static if (is(S U : U*)) // S is matched against the pattern U*
    {
        U u;
    }
    static assert(is(U == long));

The way the type of Identifier is determined is analogous to the way template parameter types are determined by TemplateTypeParameterSpecialization.

##### is ( Type Identifier == TypeSpecialization )

If TypeSpecialization is a type, the condition is satisfied if Type is semantically correct and is the same type as TypeSpecialization. Identifier is declared to be an alias of the TypeSpecialization.

    const x = 5;

    static if (is(typeof(x) T == const int))   // satisfied, T is now defined
        alias S = T;

    static assert(is(T)); // T is in scope
    pragma(msg, T); // const int

If TypeSpecialization is a type pattern involving Identifier, type deduction of Identifier is attempted based on Type. The condition is only satisfied if the type pattern is matched.

    alias Foo = long*;

    static if (is(Foo U == U*)) // Foo is matched against the pattern U*
    {
        U u;
    }
    static assert(is(U == long));

If TypeSpecialization is a valid keyword for the is(Type == Keyword) form, the condition is satisfied in the same manner. Identifier is set as follows:

keyword | alias type for Identifier
--- | ---
struct | Type
union | Type
class | Type
interface | Type
super | TypeSeq of base classes and interfaces
enum | the base type of the enum
__vector | the static array type of the vector
function | TypeSeq of the function parameter types. For C- and D-style variadic functions, only the non-variadic parameters are included. For typesafe variadic functions, the ... is ignored.
delegate | the function type of the delegate
return | the return type of the function, delegate, or function pointer
__parameters | the parameter sequence of a function, delegate, or function pointer. This includes the parameter types, names, and default values.
const | Type
immutable | Type
inout | Type
shared | Type
module | the module
package | the package

    enum E : byte { Emember }

    static if (is(E V == enum))    // satisfied, E is an enum
        V v;                       // v is declared to be a byte

    static assert(is(V == byte));

#### Parameter List Forms

    is ( Type : TypeSpecialization , TemplateParameterList )
    is ( Type == TypeSpecialization , TemplateParameterList )
    is ( Type Identifier : TypeSpecialization , TemplateParameterList )
    is ( Type Identifier == TypeSpecialization , TemplateParameterList )

More complex types can be pattern matched. The TemplateParameterList declares symbols based on the parts of the pattern that are matched, analogously to the way implied template parameters are matched.

Example: Matching a Template Instantiation

    struct Tuple(T...)
    {
        // ...
    }
    alias Tup2 = Tuple!(int, string);

    static if (is(Tup2 : Template!Args, alias Template, Args...))
    {
        static assert(__traits(isSame, Template, Tuple));
        static assert(is(Template!(int, string) == Tup2)); // same struct
    }
    static assert(is(Args[0] == int));
    static assert(is(Args[1] == string));

Type cannot be matched when TypeSpecialization is an alias template instance:

    struct S(T) {}
    alias A(T) = S!T;

    static assert(is(A!int : S!T, T));
    //static assert(!is(A!int : A!T, T));

Example: Matching an Associative Array

    alias AA = long[string];

    static if (is(AA T : T[U], U : string)) // T[U] is the pattern
    {
        pragma(msg, T);  // long
        pragma(msg, U);  // string
    }

    // no match, B is not an int
    static assert(!is(AA A : A[B], B : int));

Example: Matching a Static Array

    static if (is(int[10] W : W[len], int len)) // W[len] is the pattern
    {
        static assert(len == 10);
    }
    static assert(is(W == int));

    // no match, len should be 10
    static assert(!is(int[10] X : X[len], int len : 5));

### Rvalue Expression

    RvalueExpression:
        __rvalue ( AssignExpression )

The RvalueExpression causes the embedded AssignExpression to be treated as an rvalue whether it is an rvalue or an lvalue.

#### Overloading

If both ref and non-ref parameter overloads are present, an rvalue is preferably matched to the non-ref parameters, and an lvalue is preferably matched to the ref parameter. An RvalueExpression will preferably match with the non-ref parameter.

#### Semantics of Arguments Matched to Rvalue Parameters

An rvalue argument is owned by the function called. Hence, if an lvalue is matched to the rvalue argument, a copy is made of the lvalue to be passed to the function. The function will then call the destructor (if any) on the parameter at the conclusion of the function. An rvalue argument is not copied, as it is assumed to already be unique, and is also destroyed at the conclusion of the function.

The called function's semantics are the same whether a parameter originated as an rvalue or is a copy of an lvalue. This means that an RvalueExpression argument destroys the expression upon function return. Attempts to continue to use the lvalue expression are invalid. The compiler won't always be able to detect a use after being passed to the function, which means that the destructor for the object must reset the object's contents to its initial value, or at least a benign value that can be destructed more than once.

    struct S
    {
        ubyte* p;
        ~this()
        {
          free(p);
          // add `p = null;` here to prevent double free
        }
    }

    void aggh(S s)
    {
    }

    void oops()
    {
        S s;
        s.p = cast(ubyte*)malloc(10);
        aggh(__rvalue(s));
        // destructor of s called at end of scope, double-freeing s.p
    }

RvalueExpressions enable the use of move constructors and move assignments.

#### As a function attribute

It is also allowed to use the __rvalue keyword as a function attribute. The presence of this attribute will infer the RvalueExpression upon the function call expression at the call-site, as if applied to the function's return value. This is only accepted on functions that return by reference.

    ref S fun(return ref S s) __rvalue
    {
        return s;
    }

    S s;
    S t = fun(s); // call inferred as: __rvalue(fun(s))

### Special Keywords

    SpecialKeyword:
        __FILE__
        __FILE_FULL_PATH__
        __MODULE__
        __LINE__
        __FUNCTION__
        __PRETTY_FUNCTION__

__FILE__ and __LINE__ expand to the source file name and line number at the point of instantiation. The path of the source file is left up to the compiler.

__FILE_FULL_PATH__ expands to the absolute source file name at the point of instantiation.

__MODULE__ expands to the module name at the point of instantiation.

__FUNCTION__ expands to the fully qualified name of the function at the point of instantiation.

__PRETTY_FUNCTION__ is similar to __FUNCTION__, but also expands the function return type, its parameter types, and its attributes.

Example:

    module test;
    import std.stdio;

    void test(string file = __FILE__, size_t line = __LINE__,
            string mod = __MODULE__, string func = __FUNCTION__,
            string pretty = __PRETTY_FUNCTION__,
            string fileFullPath = __FILE_FULL_PATH__)
    {
        writefln("file: '%s', line: '%s', module: '%s',\nfunction: '%s', " ~
            "pretty function: '%s',\nfile full path: '%s'",
            file, line, mod, func, pretty, fileFullPath);
    }

    int main(string[] args)
    {
        test();
        return 0;
    }

Assuming the file was at /example/test.d, this will output:

    file: 'test.d', line: '13', module: 'test',
    function: 'test.main', pretty function: 'int test.main(string[] args)',
    file full path: '/example/test.d'

Warning: Do not use mixin(__FUNCTION__) to get the symbol for the current function. This seems to be a common thing for programmers to attempt when trying to get the symbol for the current function in order to do introspection on it, since D does not currently have a direct way to get that symbol. However, using mixin(__FUNCTION__) means that the symbol for the function will be looked up by name, which means that it's subject to the various rules that go with symbol lookup, which can cause various problems. One such problem would that if a function is overloaded, the result will be the first overload whether that's the current function or not.

Given that D doesn't currently have a way to directly get the symbol for the current function, the best way to do it is to get the parent symbol of a symbol within the function, since that avoids any issues surrounding symbol lookup rules. An example of that which doesn't rely on any other symbols is __traits(parent {}). It declares an anonymous, nested function, whose parent is then the current function. So, getting its parent gets the symbol for the current function.

## Associativity and Commutativity

An implementation may rearrange the evaluation of expressions according to arithmetic associativity and commutativity rules as long as, within that thread of execution, no observable difference is possible.

This rule precludes any associative or commutative reordering of floating point expressions.

Pragmas
Statements
Copyright © 1999-2025 by the D Language Foundation | Page generated by Ddoc on Wed Oct 1 13:16:46 2025