Inspired by this post.
The idea is simple: Extend implicit conversions to all types and values. For built-in types, one can do this: ushort(myUByte)
That works because a ubyte
can be converted to ushort
. In some cases, the static type has to be changed by explicitly requesting a conversion that is intended to be fool-proof. (A cast
would allow for narrowing or other unsafe conversions.)
D has many implicit conversions that are “obviously” correct:
- Numeric types when the range of the operand is fully included in the target type, e.g.:
ubyte
→ushort
→uint
→ulong
byte
→short
→int
→long
ubyte
→short
ushort
→int
uint
→long
float
→double
→real
- Adding
const
- Removing function attributes that make guarantees, e.g.
@safe
orpure
. - Derived to base class or interface
The idea is to allow, e.g. Type(expression)
evaluate to expression
with a static type of Type
.
Something like this is needed to aid the compiler in figuring out what you want:
interface I { }
interface J { }
class C : I, J { }
class D : I, J { }
void main()
{
I[] xs = [new C, new D]; // Error: cannot implicitly convert expression `[new C, new D]` of type `Object[]` to `I[]`
}
One solution: Use cast(I)
, but cast
is not the right tool: It results in null
for a failed cast, but we want to express the cast shouldn’t fail, and we want an error should we be mistaken.
auto xs = [I(new C), new D]; // proposed: good, typeof(xs) is `I[]`
Another examples is when you want to control what type is inferred by IFTI:
void f(T)(T x, T y) { pragma(msg, T); }
f(new C, new D); // Error: template `f` is not callable using argument types `!()(C, D)`
f(I(new C), I(new D)); // proposed: good, prints "I"
It could be implemented by lowering to ((Type __result) => __result)(expression)
.
Of course, that lowering can be provided by a function template:
auto ref R implicitCast(R, T)(auto ref T x) => x;
The main issue with that is that it’s much wordier.