Skip to content

Commit

Permalink
Merge pull request #1763 from donc/ctfeStaticPtr9745
Browse files Browse the repository at this point in the history
9744 and 9745: allow taking address of static variables in CTFE
  • Loading branch information
WalterBright committed Mar 31, 2013
2 parents 07ac092 + 4358e5a commit 7faff24
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 20 deletions.
70 changes: 56 additions & 14 deletions src/ctfeexpr.c
Expand Up @@ -574,6 +574,8 @@ Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs)
*ofs = 0;
if (e->op == TOKaddress)
e = ((AddrExp *)e)->e1;
if (e->op == TOKsymoff)
*ofs = ((SymOffExp *)e)->offset;
if (e->op == TOKdotvar)
{
Expression *ex = ((DotVarExp *)e)->e1;
Expand Down Expand Up @@ -607,11 +609,19 @@ Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs)
*/
bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2)
{
// For integers cast to pointers, we regard them as non-comparable
// unless they are identical. (This may be overly strict).
if (agg1->op == TOKint64 && agg2->op == TOKint64
&& agg1->toInteger() == agg2->toInteger())
return true;

// Note that type painting can occur with VarExp, so we
// must compare the variables being pointed to.
return agg1 == agg2 ||
(agg1->op == TOKvar && agg2->op == TOKvar &&
((VarExp *)agg1)->var == ((VarExp *)agg2)->var);
((VarExp *)agg1)->var == ((VarExp *)agg2)->var) ||
(agg1->op == TOKsymoff && agg2->op == TOKsymoff &&
((SymOffExp *)agg1)->var == ((SymOffExp *)agg2)->var);
}

// return e1 - e2 as an integer, or error if not possible
Expand All @@ -630,11 +640,16 @@ Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e
{
if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string)
{
Type *pointee = ((TypePointer *)agg1->type)->next;
dinteger_t sz = pointee->size();
return new IntegerExp(loc, (ofs1-ofs2)*sz, type);
Type *pointee = ((TypePointer *)agg1->type)->next;
dinteger_t sz = pointee->size();
return new IntegerExp(loc, (ofs1-ofs2)*sz, type);
}
}
else if (agg1->op == TOKsymoff && agg2->op == TOKsymoff &&
((SymOffExp *)agg1)->var == ((SymOffExp *)agg2)->var)
{
return new IntegerExp(loc, ofs1-ofs2, type);
}
error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract "
"pointers to two different memory blocks",
e1->toChars(), e2->toChars());
Expand All @@ -655,21 +670,36 @@ Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type,
if (eptr->op == TOKaddress)
eptr = ((AddrExp *)eptr)->e1;
Expression *agg1 = getAggregateFromPointer(eptr, &ofs1);
if (agg1->op != TOKstring && agg1->op != TOKarrayliteral)
if (agg1->op == TOKsymoff)
{
if (((SymOffExp *)agg1)->var->type->ty != Tsarray)
{
error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
return EXP_CANT_INTERPRET;
}
}
else if (agg1->op != TOKstring && agg1->op != TOKarrayliteral)
{
error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
return EXP_CANT_INTERPRET;
}
ofs2 = e2->toInteger();
Type *pointee = ((TypePointer *)agg1->type)->next;
sinteger_t indx = ofs1;
dinteger_t sz = pointee->size();
Expression *dollar = ArrayLength(Type::tsize_t, agg1);
assert(dollar != EXP_CANT_INTERPRET);
Expression *dollar;
if (agg1->op == TOKsymoff)
{
dollar = ((TypeSArray *)(((SymOffExp *)agg1)->var->type))->dim;
indx = ofs1/sz;
}
else
{
dollar = ArrayLength(Type::tsize_t, agg1);
assert(dollar != EXP_CANT_INTERPRET);
}
dinteger_t len = dollar->toInteger();

Expression *val = agg1;
TypeArray *tar = (TypeArray *)val->type;
sinteger_t indx = ofs1;
if (op == TOKadd || op == TOKaddass || op == TOKplusplus)
indx = indx + ofs2/sz;
else if (op == TOKmin || op == TOKminass || op == TOKminusminus)
Expand All @@ -679,14 +709,24 @@ Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type,
error(loc, "CTFE Internal compiler error: bad pointer operation");
return EXP_CANT_INTERPRET;
}
if (val->op != TOKarrayliteral && val->op != TOKstring)

if (indx < 0 || indx > len)
{
error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars());
error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", indx, len);
return EXP_CANT_INTERPRET;
}
if (indx < 0 || indx > len)

if (agg1->op == TOKsymoff)
{
error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", indx, len);
SymOffExp *se = new SymOffExp(loc, ((SymOffExp *)agg1)->var, indx*sz);
se->type = type;
return se;
}

Expression *val = agg1;
if (val->op != TOKarrayliteral && val->op != TOKstring)
{
error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars());
return EXP_CANT_INTERPRET;
}

Expand Down Expand Up @@ -1879,6 +1919,8 @@ bool isCtfeValueValid(Expression *newval)
{
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)
Expand Down
1 change: 1 addition & 0 deletions src/expression.h
Expand Up @@ -617,6 +617,7 @@ struct SymOffExp : SymbolExp

SymOffExp(Loc loc, Declaration *var, unsigned offset, int hasOverloads = 0);
Expression *semantic(Scope *sc);
Expression *optimize(int result, bool keepLvalue = false);
Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue);
void checkEscape();
void toCBuffer(OutBuffer *buf, HdrGenState *hgs);
Expand Down
63 changes: 58 additions & 5 deletions src/interpret.c
Expand Up @@ -1462,6 +1462,32 @@ Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal)
return EXP_CANT_INTERPRET;
}
Type *pointee = ((TypePointer *)type)->next;
if ( var->isThreadlocal())
{
error("cannot take address of thread-local variable %s at compile time", var->toChars());
return EXP_CANT_INTERPRET;
}
// Check for taking an address of a shared variable.
// If the shared variable is an array, the offset might not be zero.
VarDeclaration *vd = var->isVarDeclaration();
Type *fromType = NULL;
if (var->type->ty == Tarray || var->type->ty == Tsarray)
{
fromType = ((TypeArray *)(var->type))->next;
}
if ( var->isDataseg() && (
(offset == 0 && isSafePointerCast(var->type, pointee)) ||
(fromType && isSafePointerCast(fromType, pointee))
) && !(vd && vd->init &&
#if DMDV2
(var->isConst() || var->isImmutable())
#else
var>isConst()
#endif
))
{
return this;
}
Expression *val = getVarExp(loc, istate, var, goal);
if (val == EXP_CANT_INTERPRET)
return val;
Expand Down Expand Up @@ -3095,6 +3121,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
if (aggregate->op != TOKslice && aggregate->op != TOKstring &&
aggregate->op != TOKarrayliteral && aggregate->op != TOKassocarrayliteral)
{
if (aggregate->op == TOKsymoff)
{
error("mutable variable %s cannot be modified at compile time, even through a pointer", ((SymOffExp *)aggregate)->var->toChars());
return EXP_CANT_INTERPRET;
}
if (indexToModify != 0)
{
error("pointer index [%lld] lies outside memory block [0..1]", indexToModify);
Expand Down Expand Up @@ -3245,6 +3276,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
if (oldval->op != TOKarrayliteral && oldval->op != TOKstring
&& oldval->op != TOKslice && oldval->op != TOKnull)
{
if (oldval->op == TOKsymoff)
{
error("pointer %s cannot be sliced at compile time (it points to a static variable)", sexp->e1->toChars());
return EXP_CANT_INTERPRET;
}
if (assignmentToSlicedPointer)
{
error("pointer %s cannot be sliced at compile time (it does not point to an array)",
Expand Down Expand Up @@ -4221,6 +4257,11 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal)
}
else
{ // Pointer to a non-array variable
if (agg->op == TOKsymoff)
{
error("mutable variable %s cannot be read at compile time, even through a pointer", ((SymOffExp *)agg)->var->toChars());
return EXP_CANT_INTERPRET;
}
if ((indx + ofs) != 0)
{
error("pointer index [%lld] lies outside memory block [0..1]",
Expand Down Expand Up @@ -4374,6 +4415,11 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)
error("cannot slice null pointer %s", this->e1->toChars());
return EXP_CANT_INTERPRET;
}
if (agg->op == TOKsymoff)
{
error("slicing pointers to static variables is not supported in CTFE");
return EXP_CANT_INTERPRET;
}
if (agg->op != TOKarrayliteral && agg->op != TOKstring)
{
error("pointer %s cannot be sliced at compile time (it does not point to an array)",
Expand Down Expand Up @@ -4675,17 +4721,21 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal)
return e;
}
}
if (e1->op == TOKvar)
if (e1->op == TOKvar || e1->op == TOKsymoff)
{ // type painting operation
Type *origType = ((VarExp *)e1)->var->type;
Type *origType = (e1->op == TOKvar) ? ((VarExp *)e1)->var->type :
((SymOffExp *)e1)->var->type;
if (castBackFromVoid && !isSafePointerCast(origType, pointee))
{
error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE",
origType->toChars(), pointee->toChars());
return EXP_CANT_INTERPRET;
}
e = new VarExp(loc, ((VarExp *)e1)->var);
e->type = type;
if (e1->op == TOKvar)
e = new VarExp(loc, ((VarExp *)e1)->var);
else
e = new SymOffExp(loc, ((SymOffExp *)e1)->var, 0);
e->type = to;
return e;
}

Expand Down Expand Up @@ -4840,7 +4890,10 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal)
if (!(e->op == TOKvar || e->op == TOKdotvar || e->op == TOKindex
|| e->op == TOKslice || e->op == TOKaddress))
{
error("dereference of invalid pointer '%s'", e->toChars());
if (e->op == TOKsymoff)
error("cannot dereference pointer to static variable %s at compile time", ((SymOffExp *)e)->var->toChars());
else
error("dereference of invalid pointer '%s'", e->toChars());
return EXP_CANT_INTERPRET;
}
if (goal != ctfeNeedLvalue)
Expand Down
8 changes: 8 additions & 0 deletions src/optimize.c
Expand Up @@ -321,6 +321,14 @@ Expression *BoolExp::optimize(int result, bool keepLvalue)
return e;
}

Expression *SymOffExp::optimize(int result, bool keepLvalue)
{
assert(var);
if ((result & WANTinterpret) && var->isThreadlocal())
error("cannot take address of thread-local variable %s at compile time", var->toChars());
return this;
}

Expression *AddrExp::optimize(int result, bool keepLvalue)
{ Expression *e;

Expand Down
2 changes: 1 addition & 1 deletion src/template.c
Expand Up @@ -1661,7 +1661,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Loc loc, Scope *sc, Objec
taa->index->resolve(loc, sc, &e, &t, &s);
if (!e)
goto Lnomatch;
e = e->optimize(WANTvalue | WANTinterpret);
e = e->ctfeInterpret();
e = e->implicitCastTo(sc, Type::tsize_t);
e = e->optimize(WANTvalue);
if (!dim->equals(e))
Expand Down
60 changes: 60 additions & 0 deletions test/compilable/interpret3.d
Expand Up @@ -2344,6 +2344,66 @@ bool bug7216() {

static assert(bug7216());

/**************************************************
9745 Allow pointers to static variables
**************************************************/

shared int x9745;
shared int[5] y9745;

shared (int) * bug9745(int m)
{
auto k = &x9745;
auto j = &x9745;
auto p = &y9745[0];
auto q = &y9745[3];
assert (j - k == 0);
assert( j == k );
assert( q - p == 3);
--q;
int a = 0;
assert(p + 2 == q);
if (m==7)
{
auto z1 = y9745[0..2]; // slice global pointer
}
if (m==8)
p[1] = 7; // modify through a pointer
if (m==9)
a = p[1]; // read from a pointer
if (m == 0)
return & x9745;
return &y9745[1];
}

int test9745(int m)
{
bug9745(m);
// type painting
shared int * w = bug9745(0);
return 1;
}

shared int * w9745a = bug9745(0);
shared int * w9745b = bug9745(1);
static assert( is(typeof(compiles!(test9745(6)))));
static assert(!is(typeof(compiles!(test9745(7)))));
static assert(!is(typeof(compiles!(test9745(8)))));
static assert(!is(typeof(compiles!(test9745(9)))));

// pointers cast from an absolute address
// (mostly applies to fake pointers, eg Windows HANDLES)
bool test9745b()
{
void *b6 = cast(void *)0xFEFEFEFE;
void *b7 = cast(void *)0xFEFEFEFF;
assert(b6 is b6);
assert(b7 != b6);
return true;
}
static assert(test9745b());


/**************************************************
4065 [CTFE] AA "in" operator doesn't work
**************************************************/
Expand Down

0 comments on commit 7faff24

Please sign in to comment.