Skip to content

Commit

Permalink
Check all invalid casts in the front end, and raise semantic error fo…
Browse files Browse the repository at this point in the history
…r that

Mostly it's an emulation of CastExp::toElem(). The type categorization would work well.
  • Loading branch information
9rnsr committed May 30, 2015
1 parent 4c2a6d5 commit 77a7952
Show file tree
Hide file tree
Showing 10 changed files with 516 additions and 58 deletions.
159 changes: 117 additions & 42 deletions src/cast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1439,15 +1439,26 @@ Expression *castTo(Expression *e, Scope *sc, Type *t)
return;
}

// Do (type *) cast of (type [dim])
if (tob->ty == Tpointer &&
t1b->ty == Tsarray)
{
//printf("Converting [dim] to *\n");
result = new AddrExp(e->loc, e);
result->type = t;
return;
}
/* Make semantic error against invalid cast between concrete types.
* Assume that 'e' is never be any placeholder expressions.
* The result of these checks should be consistent with CastExp::toElem().
*/

// Fat Value types
const bool tob_isFV = (tob->ty == Tstruct || tob->ty == Tsarray);
const bool t1b_isFV = (t1b->ty == Tstruct || t1b->ty == Tsarray);

// Fat Reference types
const bool tob_isFR = (tob->ty == Tarray || tob->ty == Tdelegate);
const bool t1b_isFR = (t1b->ty == Tarray || t1b->ty == Tdelegate);

// Reference types
const bool tob_isR = (tob_isFR || tob->ty == Tpointer || tob->ty == Taarray || tob->ty == Tclass);
const bool t1b_isR = (t1b_isFR || t1b->ty == Tpointer || t1b->ty == Taarray || t1b->ty == Tclass);

// Arithmetic types (== valueable basic types)
const bool tob_isA = (tob->isintegral() || tob->isfloating());
const bool t1b_isA = (t1b->isintegral() || t1b->isfloating());

if (AggregateDeclaration *t1ad = isAggregate(t1b))
{
Expand All @@ -1460,16 +1471,14 @@ Expression *castTo(Expression *e, Scope *sc, Type *t)
ClassDeclaration *tocd = tob->isClassHandle();
int offset;
if (tocd->isBaseOf(t1cd, &offset))
goto L1;
goto Lok;
}

/* Forward the cast to our alias this member, rewrite to:
* cast(to)e1.aliasthis
*/
result = resolveAliasThis(sc, e);
result = result->castTo(sc, t);
//result = new CastExp(e->loc, ex, t);
//result = result->semantic(sc);
return;
}
}
Expand All @@ -1482,55 +1491,121 @@ Expression *castTo(Expression *e, Scope *sc, Type *t)
result = result->semantic(sc);
return;
}
else if (tob->ty != Tvector && t1b->ty == Tvector)
{
// T[n] <-- __vector(U[m])
if (tob->ty == Tsarray)
{
if (t1b->size(e->loc) == tob->size(e->loc))
goto Lok;
}
goto Lfail;
}
else if (t1b->implicitConvTo(tob) == MATCHconst && t->equals(e->type->constOf()))
{
result = e->copy();
result->type = t;
return;
}

// Bugzlla 3133: Struct casts are possible only when the sizes match
// Same with static array -> static array
if ((t1b->ty == Tsarray || t1b->ty == Tstruct) &&
(tob->ty == Tsarray || tob->ty == Tstruct))
{
if (t1b->size(e->loc) != tob->size(e->loc))
goto Lfail;
}
// Bugzilla 9178: Tsarray <--> typeof(null)
// Bugzilla 9904: Tstruct <--> typeof(null)
if (t1b->ty == Tnull && (tob->ty == Tsarray || tob->ty == Tstruct) ||
tob->ty == Tnull && (t1b->ty == Tsarray || t1b->ty == Tstruct))
// arithmetic values vs. other arithmetic values
// arithmetic values vs. T*
if (tob_isA && (t1b_isA || t1b->ty == Tpointer) ||
t1b_isA && (tob_isA || tob->ty == Tpointer))
{
goto Lfail;
goto Lok;
}
// Bugzilla 13959: Tstruct <--> Tpointer
if ((tob->ty == Tstruct && t1b->ty == Tpointer) ||
(t1b->ty == Tstruct && tob->ty == Tpointer))

// arithmetic values vs. references or fat values
if (tob_isA && (t1b_isR || t1b_isFV) ||
t1b_isA && (tob_isR || tob_isFV))
{
goto Lfail;
}
// Bugzilla 14596: Tpointer --> T(s)array
if ((tob->ty == Tarray || tob->ty == Tsarray) && t1b->ty == Tpointer)

// Bugzlla 3133: A cast between fat values is possible only when the sizes match.
if (tob_isFV && t1b_isFV)
{
goto Lfail;
if (t1b->size(e->loc) == tob->size(e->loc))
goto Lok;
e->error("cannot cast expression %s of type %s to %s because of different sizes",
e->toChars(), e->type->toChars(), t->toChars());
result = new ErrorExp();
return;
}

// Bugzilla 10646: Tclass <--> (T[] or T[n])
if (tob->ty == Tclass && (t1b->ty == Tarray || t1b->ty == Tsarray) ||
t1b->ty == Tclass && (tob->ty == Tarray || tob->ty == Tsarray))
// Fat values vs. null or references
if (tob_isFV && (t1b->ty == Tnull || t1b_isR) ||
t1b_isFV && (tob->ty == Tnull || tob_isR))
{
if (tob->ty == Tpointer && t1b->ty == Tsarray)
{
// T[n] sa;
// cast(U*)sa; // ==> cast(U*)sa.ptr;
result = new AddrExp(e->loc, e);
result->type = t;
return;
}
if (tob->ty == Tarray && t1b->ty == Tsarray)
{
// T[n] sa;
// cast(U[])sa; // ==> cast(U[])sa[];
d_uns64 fsize = t1b->nextOf()->size();
d_uns64 tsize = tob->nextOf()->size();
if ((((TypeSArray *)t1b)->dim->toInteger() * fsize) % tsize != 0)
{
// copied from sarray_toDarray() in e2ir.c
e->error("cannot cast expression %s of type %s to %s since sizes don't line up",
e->toChars(), e->type->toChars(), t->toChars());
result = new ErrorExp();
return;
}
goto Lok;
}
goto Lfail;
}
// Bugzilla 11484: (T[] or T[n]) <--> TypeBasic
// Bugzilla 11485, 7472: Tclass <--> TypeBasic
// Bugzilla 14154L Tstruct <--> TypeBasic
if (t1b->isTypeBasic() && (tob->ty == Tarray || tob->ty == Tsarray || tob->ty == Tclass || tob->ty == Tstruct) ||
tob->isTypeBasic() && (t1b->ty == Tarray || t1b->ty == Tsarray || t1b->ty == Tclass || t1b->ty == Tstruct))
{

/* For references, any reinterpret casts are allowed to same 'ty' type.
* T* to U*
* R1 function(P1) to R2 function(P2)
* R1 delegate(P1) to R2 delegate(P2)
* T[] to U[]
* V1[K1] to V2[K2]
* class/interface A to B (will be a dynamic cast if possible)
*/
if (tob->ty == t1b->ty && tob_isR && t1b_isR)
goto Lok;

// typeof(null) <-- non-null references or values
if (tob->ty == Tnull && t1b->ty != Tnull)
goto Lfail; // Bugzilla 14629
// typeof(null) --> non-null references or arithmetic values
if (t1b->ty == Tnull && tob->ty != Tnull)
goto Lok;

// Check size mismatch of references.
// Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof.
if (tob_isFR && t1b_isR ||
t1b_isFR && tob_isR)
{
if (tob->ty == Tpointer && t1b->ty == Tarray)
{
// T[] da;
// cast(U*)da; // ==> cast(U*)da.ptr;
goto Lok;
}
if (tob->ty == Tpointer && t1b->ty == Tdelegate)
{
// void delegate() dg;
// cast(U*)dg; // ==> cast(U*)dg.ptr;
// Note that it happens even when U is a Tfunction!
e->deprecation("casting from %s to %s is deprecated", e->type->toChars(), t->toChars());
goto Lok;
}
goto Lfail;
}
if (t1b->ty == Tvoid && tob->ty != Tvoid && e->op != TOKfunction)

if (t1b->ty == Tvoid && tob->ty != Tvoid)
{
Lfail:
e->error("cannot cast expression %s of type %s to %s",
Expand All @@ -1539,7 +1614,7 @@ Expression *castTo(Expression *e, Scope *sc, Type *t)
return;
}

L1:
Lok:
result = new CastExp(e->loc, e, tob);
result->type = t; // Don't call semantic()
//printf("Returning: %s\n", result->toChars());
Expand Down
4 changes: 4 additions & 0 deletions src/constfold.c
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,11 @@ UnionExp Cast(Type *type, Type *to, Expression *e1)
else
{
if (type != Type::terror)
{
// have to change to Internal Compiler Error
// all invalid casts should be handled already in Expression::castTo().
error(loc, "cannot cast %s to %s", e1->type->toChars(), type->toChars());
}
new(&ue) ErrorExp();
}
return ue;
Expand Down
1 change: 1 addition & 0 deletions src/e2ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ elem *sarray_toDarray(Loc loc, Type *tfrom, Type *tto, elem *e)

if ((dim * fsize) % tsize != 0)
{
// have to change to Internal Compiler Error?
error(loc, "cannot cast %s to %s since sizes don't line up", tfrom->toChars(), tto->toChars());
}
dim = (dim * fsize) / tsize;
Expand Down
4 changes: 0 additions & 4 deletions src/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -9738,10 +9738,6 @@ Expression *CastExp::semantic(Scope *sc)
return new VectorExp(loc, e1, to);
}

if (tob->ty == Tpointer && t1b->ty == Tdelegate)
deprecation("casting from %s to %s is deprecated", e1->type->toChars(), to->toChars());


Expression *ex = e1->castTo(sc, to);
if (ex->op == TOKerror)
return ex;
Expand Down
16 changes: 9 additions & 7 deletions test/fail_compilation/fail233.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
void bug1176(){
// REQUIRED_ARGS: -o-
/*
Error: void does not have a default initializer
Error: integral constant must be scalar type, not void
Error: cannot implicitly convert expression (0) of type int to const(void[])
Error: cannot cast int to const(void[])
Error: integral constant must be scalar type, not const(void[])
TEST_OUTPUT:
---
fail_compilation/fail233.d(11): Error: variable fail233.bug1176.v void[1] does not have a default initializer
---
*/
void[1] v;

void bug1176()
{
void[1] v;
}
2 changes: 1 addition & 1 deletion test/fail_compilation/fail304.d
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail304.d(15): Error: cannot cast expression foo() of type Small to Large
fail_compilation/fail304.d(15): Error: cannot cast expression foo() of type Small to Large because of different sizes
---
*/

Expand Down
4 changes: 2 additions & 2 deletions test/fail_compilation/fail8179b.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// REQUIRED_ARGS: -o-
/*
TEST_OUTPUT:
---
fail_compilation/fail8179b.d(10): Error: e2ir: cannot cast [1, 2] of type int[] to type int[2][1]
fail_compilation/fail8179b.d(10): Error: cannot cast expression [1, 2] of type int[] to int[2][1]
---
*/

void foo(int[2][1]) {}
void main() {
foo(cast(int[2][1])[1, 2]);
Expand Down
4 changes: 2 additions & 2 deletions test/fail_compilation/fail_casting.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail_casting.d(12): Error: cannot cast expression x of type short[2] to int[2]
fail_compilation/fail_casting.d(12): Error: cannot cast expression x of type short[2] to int[2] because of different sizes
---
*/
void test3133()
Expand Down Expand Up @@ -63,7 +63,7 @@ fail_compilation/fail_casting.d(77): Error: cannot cast expression x of type flo
fail_compilation/fail_casting.d(78): Error: cannot cast expression x of type int to float[]
---
*/
void tst11484()
void test11484()
{
// Tsarray <--> integer
{ int[1] x; auto y = cast(int ) x; }
Expand Down
Loading

0 comments on commit 77a7952

Please sign in to comment.