Skip to content

Commit

Permalink
Merge pull request #2360 from donc/delegatecmp10452ctfe
Browse files Browse the repository at this point in the history
10452  CTFE: Cannot compare delegates with == or 'is'
  • Loading branch information
WalterBright committed Jul 21, 2013
2 parents 97c624f + 48385f9 commit 01c8a41
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 7 deletions.
71 changes: 66 additions & 5 deletions src/ctfeexpr.c
Expand Up @@ -1111,6 +1111,8 @@ bool isCtfeComparable(Expression *e)
if (x->isConst() != 1 &&
x->op != TOKnull &&
x->op != TOKstring &&
x->op != TOKfunction &&
x->op != TOKdelegate &&
x->op != TOKarrayliteral &&
x->op != TOKstructliteral &&
x->op != TOKclassreference)
Expand Down Expand Up @@ -1308,6 +1310,21 @@ int ctfeCmpArrays(Loc loc, Expression *e1, Expression *e2, uinteger_t len)
return 0;
}

/* Given a delegate expression e, return .funcptr.
* If e is NullExp, return NULL.
*/
FuncDeclaration *funcptrOf(Expression *e)
{
assert(e->type->ty == Tdelegate);

if (e->op == TOKdelegate)
return ((DelegateExp *)e)->func;
if (e->op == TOKfunction)
return ((FuncExp *)e)->fd;
assert(e->op == TOKnull);
return NULL;
}

bool isArray(Expression *e)
{
return e->op == TOKarrayliteral || e->op == TOKstring ||
Expand All @@ -1325,13 +1342,16 @@ int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2)
return 0;
return 1;
}

// null == null, regardless of type

if (e1->op == TOKnull && e2->op == TOKnull)
return 0;

if (e1->type->ty == Tpointer && e2->type->ty == Tpointer)
{ // Can only be an equality test.
if (e1->op == TOKnull && e2->op == TOKnull)
return 0;
{
// Can only be an equality test.

dinteger_t ofs1, ofs2;
Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
Expand All @@ -1342,6 +1362,35 @@ int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2)
}
return 1;
}
if (e1->type->ty == Tdelegate && e2->type->ty == Tdelegate)
{
// If .funcptr isn't the same, they are not equal

if (funcptrOf(e1) != funcptrOf(e2))
return 1;

// If both are delegate literals, assume they have the
// same closure pointer. TODO: We don't support closures yet!
if (e1->op == TOKfunction && e2->op == TOKfunction)
return 0;
assert(e1->op == TOKdelegate && e2->op == TOKdelegate);

// Same .funcptr. Do they have the same .ptr?
Expression * ptr1 = ((DelegateExp *)e1)->e1;
Expression * ptr2 = ((DelegateExp *)e2)->e1;

dinteger_t ofs1, ofs2;
Expression *agg1 = getAggregateFromPointer(ptr1, &ofs1);
Expression *agg2 = getAggregateFromPointer(ptr2, &ofs2);
// If they are TOKvar, it means they are FuncDeclarations
if ((agg1 == agg2 && ofs1 == ofs2) ||
(agg1->op == TOKvar && agg2->op == TOKvar &&
((VarExp *)agg1)->var == ((VarExp *)agg2)->var))
{
return 0;
}
return 1;
}
if (isArray(e1) && isArray(e2))
{
uinteger_t len1 = resolveArrayLength(e1);
Expand Down Expand Up @@ -1984,15 +2033,27 @@ bool isCtfeValueValid(Expression *newval)
if (ie->e2->op == TOKvar)
return true;
}
if (newval->op == TOKfunction) return true; // function/delegate literal
if (newval->op == TOKdelegate) return true;

if (newval->op == TOKfunction)
return true; // function literal or delegate literal

if (newval->op == TOKdelegate)
{
Expression *dge = ((DelegateExp *)newval)->e1;
if (dge->op == TOKvar && ((VarExp *)dge)->var->isFuncDeclaration())
return true; // &nestedfunc

if (dge->op == TOKstructliteral || dge->op == TOKclassreference)
return true; // &struct.func or &clasinst.func
}
if (newval->op == TOKsymoff) // function pointer
{
if (((SymOffExp *)newval)->var->isFuncDeclaration())
return true;
if (((SymOffExp *)newval)->var->isDataseg())
return true; // pointer to static variable
}

if (newval->op == TOKint64 || newval->op == TOKfloat64 ||
newval->op == TOKchar || newval->op == TOKcomplex80)
return true;
Expand Down
22 changes: 21 additions & 1 deletion src/interpret.c
Expand Up @@ -2085,7 +2085,27 @@ Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal)
#if LOG
printf("%s DelegateExp::interpret() %s\n", loc.toChars(), toChars());
#endif
return this;
// TODO: Really we should create a CTFE-only delegate expression
// of a pointer and a funcptr.

// If it is &nestedfunc, just return it
// TODO: We should save the context pointer
if (e1->op == TOKvar && ((VarExp *)e1)->var->isFuncDeclaration())
return this;

// If it has already been CTFE'd, just return it
if (e1->op == TOKstructliteral || e1->op == TOKclassreference)
return this;

// Else change it into &structliteral.func or &classref.func
Expression *e = e1->interpret(istate, ctfeNeedLvalue);

if (exceptionOrCantInterpret(e))
return e;

e = new DelegateExp(loc, e, func);
e->type = type;
return e;
}


Expand Down
67 changes: 66 additions & 1 deletion test/compilable/interpret3.d
Expand Up @@ -4985,6 +4985,71 @@ void ice9445(void delegate() expr, void function() f2)
static assert(!is(typeof(c9445!(expr()))));
}

/**************************************************
10452 delegate ==
**************************************************/

struct S10452 {
bool func() { return true; }
}

struct Outer10452 {
S10452 inner;
}

class C10452 {
bool func() { return true; }
}

bool delegate() ref10452(ref S10452 s)
{
return &s.func;
}

bool test10452()
{
bool delegate() bar = () { return true; };

assert(bar !is null);
assert(bar is bar);

S10452 bag;
S10452[6] bad;
Outer10452 outer;
C10452 tag = new C10452;

auto rat = &outer.inner.func;
assert(rat == rat);
auto tat = &tag.func;
assert(tat == tat);

auto bat = &outer.inner.func;
auto mat = &bad[2].func;
assert(mat is mat);
assert(rat == bat);

auto zat = &bag.func;
auto cat = &bag.func;
assert(zat == zat);
assert(zat == cat);

auto drat = ref10452(bag);
assert(cat == drat);
assert(drat == drat);
drat = ref10452(bad[2]);
assert( drat == mat);
assert(tat != rat);
assert(zat != rat);
assert(rat != cat);
assert(zat != bar);
assert(tat != cat);
cat = bar;
assert(cat == bar);
return true;
}
static assert(test10452());


/**************************************************
7162 and 4711
**************************************************/
Expand Down Expand Up @@ -5543,7 +5608,7 @@ static assert(!__traits(compiles, {static Test76 t76 = new Test76(0); return t76

/***** Bug 5678 *********************************/

struct Bug5678
struct Bug5678
{
this(int) {}
}
Expand Down

0 comments on commit 01c8a41

Please sign in to comment.