Skip to content

Commit

Permalink
Add logic to decipher floating point implicit and explicit casts.
Browse files Browse the repository at this point in the history
Logic is consistent with dmd with the exception that dmd seems to allow some casts *to* void which are not yet accounted for (if ever)

Unit tests have been added.
  • Loading branch information
maxhaton committed May 31, 2023
1 parent aa64387 commit c27797e
Showing 1 changed file with 118 additions and 2 deletions.
120 changes: 118 additions & 2 deletions src/d/semantic/caster.d
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,11 @@ struct Caster(bool isExplicit, alias bailoutOverride = null) {
return CastKind.UPad;
}

return CastKind.Invalid;
if (isFloat(bt)) {
return CastKind.SignedToFloat;
} else {
return CastKind.Invalid;
}

case Char:
t = integralOfChar(t);
Expand All @@ -319,6 +323,12 @@ struct Caster(bool isExplicit, alias bailoutOverride = null) {
return CastKind.IntToBool;
}

if (isFloat(bt)) {
return isSigned(t)
? CastKind.SignedToFloat
: CastKind.UnsignedToFloat;
}

if (!isIntegral(bt)) {
return CastKind.Invalid;
}
Expand All @@ -336,7 +346,23 @@ struct Caster(bool isExplicit, alias bailoutOverride = null) {
}

case Float, Double, Real:
assert(0, "Floating point casts are not implemented");
// cast from a float to a float
if (isFloat(bt)) {
assert(bt != t);
return bt < t ? CastKind.FloatTrunc : CastKind.FloatExtend;
}

if (isExplicit) {
if (!isIntegral(bt) && !canConvertToIntegral(bt)) {
return CastKind.Invalid;
}

return isSigned(isChar(bt) ? integralOfChar(bt) : bt)
? CastKind.FloatToSigned
: CastKind.FloatToUnsigned;
} else {
return bailout(t);
}

case Null:
return CastKind.Invalid;
Expand Down Expand Up @@ -650,3 +676,93 @@ struct Caster(bool isExplicit, alias bailoutOverride = null) {
return CastKind.Invalid;
}
}

version(unittest) {
BuiltinType[] floatTypes =
[BuiltinType.Float, BuiltinType.Double, BuiltinType.Real];
BuiltinType[] signedTypes =
[BuiltinType.Byte, BuiltinType.Short, BuiltinType.Int, BuiltinType.Long,
BuiltinType.Cent];
BuiltinType[] unsignedTypes =
[BuiltinType.Ubyte, BuiltinType.Ushort, BuiltinType.Uint,
BuiltinType.Ulong, BuiltinType.Ucent];
void checkCastKind(
const BuiltinType from,
const BuiltinType to,
const CastKind explicitShouldBe,
const CastKind implicitShouldBe
) {
Type fromAsType = Type.get(from);
Type toAsType = Type.get(to);
import std.format : format;
const explicitResult = explicitCastFrom(null, fromAsType, toAsType);
assert(
explicitResult == explicitShouldBe,
format("Explicit cast yielded `%s`, when `%s` was expected",
explicitResult, explicitShouldBe)
);
const implicitResult = implicitCastFrom(null, fromAsType, toAsType);
assert(
implicitResult == implicitShouldBe,
format("Implicit cast yielded `%s`, when `%s` was expected",
implicitResult, implicitShouldBe)
);
}
}

@("Float to float casts")
unittest {
foreach (from; floatTypes)
foreach (to; floatTypes) {
const shouldBe = from == to
? CastKind.Exact
: (from < to ? CastKind.FloatExtend : CastKind.FloatTrunc);
checkCastKind(from, to, shouldBe, shouldBe);
}
}

@("Bools can be casted to floats")
unittest {
foreach (to; floatTypes) {
checkCastKind(BuiltinType.Bool, to, CastKind.UnsignedToFloat,
CastKind.UnsignedToFloat);
}
}

@("Signed types can be casted to floats")
unittest {
foreach (from; signedTypes) {
foreach (to; floatTypes) {
checkCastKind(from, to, CastKind.SignedToFloat,
CastKind.SignedToFloat);
}
}
}

@("Unsigned types can be casted to floats")
unittest {
foreach (from; unsignedTypes) {
foreach (to; floatTypes) {
checkCastKind(from, to, CastKind.UnsignedToFloat,
CastKind.UnsignedToFloat);
}
}
}

@("Float casts to signed types")
unittest {
foreach (from; floatTypes) {
foreach (to; signedTypes) {
checkCastKind(from, to, CastKind.FloatToSigned, CastKind.Invalid);
}
}
}

@("Float casts to unsigned types")
unittest {
foreach (from; floatTypes) {
foreach (to; unsignedTypes) {
checkCastKind(from, to, CastKind.FloatToUnsigned, CastKind.Invalid);
}
}
}

0 comments on commit c27797e

Please sign in to comment.