D follows the C integral promotion rules, with a couple crucial modifications:
1. No implicit conversions are done that throw away information - those will require an explicit cast. For example:
int i = 1999;
char c = i; // not allowed
char d = cast(char)i; // explicit cast
2. The compiler keeps track of the range of values an expression could have, and allows narrowing conversions when they can be proven to not lose information. For example: int i = 1999;
char c = i & 0xFF; // allowed
The idea is to safely avoid needing casts, in order to avoid the bugs that silently creep in with refactoring.Continuing with the notion that casts should be avoided where practical is the cast expression has its own keyword. This makes casting greppable, so the code review can find them. C casts require a C parser with lookahead to find.
One other difference: D's integer types have fixed sizes. A char is 8 bits, a short is 16, an int is 32, and a long is 64. This is based on my experience that a vast amount of C programming time is spent trying to account for the implementation-defined sizes of the integer types. As a result, D code out of the box tends to be far more portable than C.
D also defines integer math as 2's complement arithmetic. All that 1's complement stuff belongs in the dustbin of history.