Skip to content

Commit

Permalink
Merge pull request #4691 from 9rnsr/fix_casting
Browse files Browse the repository at this point in the history
Exhaustive fix for minor ICE "e2ir: cannot cast" (and fixes one reopened regression)
  • Loading branch information
WalterBright committed May 31, 2015
2 parents 26ebfeb + aefeb3f commit 1857799
Show file tree
Hide file tree
Showing 10 changed files with 545 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
33 changes: 31 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 Expand Up @@ -192,3 +192,32 @@ void test14596()
auto arr = cast(char[])p;
char[2] sarr = cast(char[2])p;
}

/*
TEST_OUTPUT:
---
fail_compilation/fail_casting.d(217): Error: cannot cast expression c of type fail_casting.test14629.C to typeof(null)
fail_compilation/fail_casting.d(218): Error: cannot cast expression p of type int* to typeof(null)
fail_compilation/fail_casting.d(219): Error: cannot cast expression da of type int[] to typeof(null)
fail_compilation/fail_casting.d(220): Error: cannot cast expression aa of type int[int] to typeof(null)
fail_compilation/fail_casting.d(221): Error: cannot cast expression fp of type int function() to typeof(null)
fail_compilation/fail_casting.d(222): Error: cannot cast expression dg of type int delegate() to typeof(null)
---
*/
void test14629()
{
alias P = int*; P p;
alias DA = int[]; DA da;
alias AA = int[int]; AA aa;
alias FP = int function(); FP fp;
alias DG = int delegate(); DG dg;
class C {} C c;
alias N = typeof(null);

{ auto x = cast(N)c; }
{ auto x = cast(N)p; }
{ auto x = cast(N)da; }
{ auto x = cast(N)aa; }
{ auto x = cast(N)fp; }
{ auto x = cast(N)dg; }
}
Loading

0 comments on commit 1857799

Please sign in to comment.