May 14, 2020
I have a container which provides access to data via a handle. For book keeping I calculate some bitmasks.
Previously, when the handle type as well as the constants were uint, everything compiled fine.
Today I added a template parameter to be able to specify the handle type and I ran into this problem.

I've read the following information on the matter:

https://forum.dlang.org/thread/yqfhytyhivltamujdhgb@forum.dlang.org
https://issues.dlang.org/show_bug.cgi?id=18380
https://dlang.org/changelog/2.078.0.html#fix16997

However I still don't see the problem with regards to unsigned types.
Why is it necessary to promote a ushort or ubyte to int for the purpose of shifting or the one's complement ?
At least the code at the bottom of the post seems to produce correct results.

One problem I see with the bug fix is that, AFAIK, the int type in C is not a fixed bit type like it is in D where it is defined to be 32 bit and therefore casting to int in D can't really reproduce the C behavior. What  am I missing ?

Back to my container.
* Using a 32 bit type, i.e. uint, everything compiles fine.

* Using a 16 or 8 bit type, i.e. ushort and ubyte, the compiler complains with -preview=intpromote for the pragmas and errors out on assignments of the same types.
i.e. e.g.

alias handle_t = ushort;
enum handle_t MASK = 0x8000;
handle_t handle = ~MASK; // the error message is basically: [value is promoted to int and] 32769 can't be assigned to ushort

* Using a 64 bit type, i.e. ulong, the whole thing blows up because the compiler pro-, or rather, demotes the ulong to int and int << 40 is obviously violating a constraint of 0..31 for 32bit types.

void main()
{
    import std.conv: to;
    import std.stdio;
    alias t = ushort;
    enum t m = 0x8000;
    pragma (msg, m.to!string(16));
    pragma (msg, (~m).to!string(16));
    pragma (msg, (cast(int)m).to!string(16));
    pragma (msg, (~cast(int)m).to!string(16));
}

2.063  : Success with output:
-----
8000
7FFF
8000
FFFFFFFFFFFF7FFF
-----

2.064   to 2.077.1: Success with output:
-----
8000
7FFF
8000
FFFF7FFF
-----

2.078.1 to 2.084.1: Success with output:
-----
8000
onlineapp.d(8): Deprecation: integral promotion not done for `~cast(ushort)32768u`, use '-transition=intpromote' switch or `~cast(int)(cast(ushort)32768u)`
7FFF
8000
FFFF7FFF
-----

Apart from the fact that I end up with an int, which causes all kinds of havoc and the annoyance that i need to cast a ushort to ushort to be able to assign it to a ushort, it appears to me that all the results are correct.