Search⌘ K
AI Features

Automatic Type Conversions

Explore how automatic type conversions work in the D programming language, including implicit conversions during arithmetic and function calls. Understand integer promotions and how the compiler safely manages type compatibility without modifying original variables.

Variables must be compatible with the expressions that they take part in. As it has probably been obvious from the programs that we have seen so far, D is a statically typed language, meaning that the compatibility of types is validated at compile time.

All of the expressions that we have written so far always had compatible types because otherwise the code would be rejected by the compiler. The following is an example of code that has incompatible types:

svg viewer
D
import std.stdio;
void main() {
char[] slice;
writeln(slice + 5); // ← compilation ERROR
}

The compiler rejects the code due to the incompatible types char[] and int for the addition operation.

Type incompatibility does not mean that the types are different; different types can indeed be used in expressions safely. For example, an int variable can safely be used in place of a double value:

D
import std.stdio;
void main() {
double sum = 1.25;
int increment = 3;
sum += increment;
write(sum);
}

Even though sum and increment are of different types, the code above is valid because incrementing a double variable by an int value is legal.

Automatic type conversions

Automatic type conversions are also called implicit type conversions.

Conversions for arithmetic operations

Although double and int are compatible types in the expression above, the addition operation must still be evaluated as a specific type at the microprocessor level. As you already know, the 64-bit type double is wider (or larger) than the 32-bit type int. Additionally, any value that fits in an int also fits in a double.

When the compiler encounters an expression that involves mismatched types, it first converts the parts of the expressions to a common type and then evaluates the overall expression. The automatic conversions that are performed by the compiler are in the direction that avoids data loss. For example, double can hold any value that int can hold but the opposite is not true. The += operation above can work because any int value can safely be converted to double.

The value that has been generated automatically as a result of a conversion is always an anonymous (and often temporary) variable. The original value does not change. For example, the automatic conversion during += above does not change the type of increment; it is always an int. Rather, a temporary value of type double is constructed with the value of increment. The conversion that takes place in the background is equivalent to the following code:

D
import std.stdio;
void main() {
double sum = 1.25;
int increment = 3;
double an_anonymous_double_value = increment;
sum += an_anonymous_double_value;
write(sum);
}

The compiler converts the int value to a temporary double value and uses that value in the operation.

In this example, the temporary variable lives only during the += operation.

Conversions for functions

Automatic conversions are not limited to arithmetic operations. There are other cases where types are converted to other types automatically. As long as the conversions are valid, the compiler takes advantage of type conversions to be able to use values in expressions. For example, a byte value can be passed for an int parameter:

D
import std.stdio;
void func(int number) {
write(number);
// ...
}
void main() {
byte smallValue = 7;
func(smallValue);
// automatic type conversion
}

In the code above, first a temporary int value is constructed and the function is called with that value.

Integer promotions

Values of types that are on the left-hand side of the following table never take part in arithmetic expressions as their actual types. Each type is first promoted to the type that is on the right-hand side of the table.

From To
bool int
byte int
ubyte int
short int
ushort int
char int
wchar int
dchar uint

Integer promotions are applied to enum values as well.

The reasons for integer promotions are both historical (where the rules come from C) and the fact that the natural arithmetic type for the microprocessor is int. For example, although the following two variables are both ubyte, the addition operation is performed only after both of the values are individually promoted to int:

D
import std.stdio;
void main() {
ubyte a = 1;
ubyte b = 2;
writeln(typeof(a + b).stringof); // the addition is not in ubyte
}

Note that the types of the variables a and b do not change; only their values are temporarily promoted to int for the duration of the addition operation.