Showing with 64 additions and 7 deletions.
  1. +34 −5 src/expression.d
  2. +8 −1 src/statement.d
  3. +1 −1 test/fail_compilation/fail325.d
  4. +21 −0 test/fail_compilation/fail_casting2.d
39 changes: 34 additions & 5 deletions src/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -10225,17 +10225,20 @@ public:
//static int x; assert(++x < 10);
if (type)
return this;

if (Expression ex = unaSemantic(sc))
return ex;
Expression e1x = resolveProperties(sc, e1);
if (e1x.op == TOKerror)
return e1x;
e1 = e1x;

if (!e1.type)
{
error("cannot cast %s", e1.toChars());
return new ErrorExp();
}

if (!to) // Handle cast(const) and cast(immutable), etc.
to = e1.type.castMod(mod);
to = to.semantic(loc, sc);
Expand All @@ -10246,73 +10249,93 @@ public:
error("cannot cast %s to tuple type %s", e1.toChars(), to.toChars());
return new ErrorExp();
}
if (e1.op == TOKtemplate)

if (e1.type.ty != Tvoid ||
e1.op == TOKfunction && to.ty == Tvoid ||
e1.op == TOKtype ||
e1.op == TOKtemplate)
{
error("cannot cast template %s to type %s", e1.toChars(), to.toChars());
return new ErrorExp();
if (e1.checkValue())
return new ErrorExp();
}

// cast(void) is used to mark e1 as unused, so it is safe
if (to.ty == Tvoid)
{
type = to;
return this;
}

if (!to.equals(e1.type) && mod == cast(ubyte)~0)
{
if (Expression e = op_overload(sc))
return e.implicitCastTo(sc, to);
}

Type t1b = e1.type.toBasetype();
Type tob = to.toBasetype();

if (tob.ty == Tstruct && !tob.equals(t1b))
{
/* Look to replace:
* cast(S)t
* with:
* S(t)
*/

// Rewrite as to.call(e1)
Expression e = new TypeExp(loc, to);
e = new CallExp(loc, e, e1);
e = e.trySemantic(sc);
if (e)
return e;
}

if (!t1b.equals(tob) && (t1b.ty == Tarray || t1b.ty == Tsarray))
{
if (checkNonAssignmentArrayOp(e1))
return new ErrorExp();
}

// Look for casting to a vector type
if (tob.ty == Tvector && t1b.ty != Tvector)
{
return new VectorExp(loc, e1, to);
}

Expression ex = e1.castTo(sc, to);
if (ex.op == TOKerror)
return ex;

// Check for unsafe casts
if (sc.func && !sc.intypeof)
{
// Disallow unsafe casts

// Implicit conversions are always safe
if (t1b.implicitConvTo(tob))
goto Lsafe;

if (!tob.hasPointers())
goto Lsafe;

if (tob.ty == Tclass && t1b.ty == Tclass)
{
ClassDeclaration cdfrom = t1b.isClassHandle();
ClassDeclaration cdto = tob.isClassHandle();

int offset;
if (!cdfrom.isBaseOf(cdto, &offset))
goto Lunsafe;

if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
goto Lunsafe;

if (!MODimplicitConv(t1b.mod, tob.mod))
goto Lunsafe;
goto Lsafe;
}

if (tob.ty == Tarray && t1b.ty == Tsarray) // Bugzilla 12502
t1b = t1b.nextOf().arrayOf();
if (tob.ty == Tarray && t1b.ty == Tarray)
Expand All @@ -10327,19 +10350,25 @@ public:
Type tobn = tob.nextOf().toBasetype();
Type t1bn = t1b.nextOf().toBasetype();
// If the struct is opaque we don't know about the struct members and the cast becomes unsafe
bool sfwrd = tobn.ty == Tstruct && !(cast(StructDeclaration)(cast(TypeStruct)tobn).sym).members || t1bn.ty == Tstruct && !(cast(StructDeclaration)(cast(TypeStruct)t1bn).sym).members;
if (!sfwrd && !tobn.hasPointers() && tobn.ty != Tfunction && t1bn.ty != Tfunction && tobn.size() <= t1bn.size() && MODimplicitConv(t1bn.mod, tobn.mod))
bool sfwrd = tobn.ty == Tstruct && !(cast(TypeStruct)tobn).sym.members ||
t1bn.ty == Tstruct && !(cast(TypeStruct)t1bn).sym.members;
if (!sfwrd && !tobn.hasPointers() &&
tobn.ty != Tfunction && t1bn.ty != Tfunction &&
tobn.size() <= t1bn.size() &&
MODimplicitConv(t1bn.mod, tobn.mod))
{
goto Lsafe;
}
}

Lunsafe:
if (sc.func.setUnsafe())
{
error("cast from %s to %s not allowed in safe code", e1.type.toChars(), to.toChars());
return new ErrorExp();
}
}

Lsafe:
return ex;
}
Expand Down
9 changes: 8 additions & 1 deletion src/statement.d
Original file line number Diff line number Diff line change
Expand Up @@ -4240,20 +4240,26 @@ public:
exp = inferType(exp, tret);
else if (fld && fld.treq)
exp = inferType(exp, fld.treq.nextOf().nextOf());

exp = exp.semantic(sc);
exp = resolveProperties(sc, exp);
if (exp.type && exp.type.ty != Tvoid || exp.op == TOKfunction || exp.op == TOKtype || exp.op == TOKtemplate)
if (exp.type && exp.type.ty != Tvoid ||
exp.op == TOKfunction ||
exp.op == TOKtype ||
exp.op == TOKtemplate)
{
// don't make error for void expression
if (exp.checkValue())
exp = new ErrorExp();
}
if (checkNonAssignmentArrayOp(exp))
exp = new ErrorExp();

// Extract side-effect part
exp = Expression.extractLast(exp, &e0);
if (exp.op == TOKcall)
exp = valueNoDtor(exp);

/* Void-return function can have void typed expression
* on return statement.
*/
Expand All @@ -4266,6 +4272,7 @@ public:
exp = new CastExp(loc, exp, Type.tvoid);
exp = exp.semantic(sc);
}

/* Replace:
* return exp;
* with:
Expand Down
2 changes: 1 addition & 1 deletion test/fail_compilation/fail325.d
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail325.d(12): Error: cannot cast template fun(T = int)(int w, int z) to type void function(int, int)
fail_compilation/fail325.d(12): Error: template fun(T = int)(int w, int z) has no value
---
*/

Expand Down
21 changes: 21 additions & 0 deletions test/fail_compilation/fail_casting2.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// REQUIRED_ARGS: -o-

/*
TEST_OUTPUT:
---
fail_compilation/fail_casting2.d(15): Error: type int has no value
fail_compilation/fail_casting2.d(17): Error: template lambda has no value
fail_compilation/fail_casting2.d(20): Error: template Templ() has no value
---
*/

void test15214()
{
alias Type = int;
cast(void)(Type);

cast(void)(x => mixin(x)("mixin(x);"));

template Templ() {}
cast(void)(Templ);
}